Blog
Ryan Moscoe
Software Engineer | AI Prompt Engineer | Ninja
- Blog
- Software Engineering
- Will Hunting Challenge
-
Toying with Operators
Toying with Operators
Oops! Something went wrong. Please try again later.
My #WillHuntingChallenge continues! I've finished a lesson on recursion, and I'm starting to get into classes and OOP. These topics are not new to me, but I took away a really cool nugget: in Python, you can change the behavior of operators like "+", "+=", and "<" for your class using dunder methods.
Of course, it's amusing to consider making the "+" operator do subtraction, but the real value of this ability is apparent when creating data structures that don't exist natively (at least not without an import), such as queues and stacks. Or maybe you just want a variation of a list that only accepts elements of a certain data type or has a fixed length, like a traditional array. Creating these data structures is a fairly basic skill commonly taught in CS college programs and coding boot camps, but what happens when you want to concatenate two of these structures or concatenate a list or tuple onto a custom data structure?
By default, something like my_struct + my_list, where my_struct is an instance of a custom data type, would produce an error. After all, how would the Python interpreter know which attribute of my_struct you want to use in this addition? And even if it could guess that, how would it know what you want it to do when the data types of that attribute and my_list (a list) don't match? You would run into a similar issue when trying to use built-in functions like str() on your structure, and I knew you could use dunder methods to make custom class instances play nicely with built-in functions--but it turns out you can give the interpreter instructions for operator behavior as well by defining specific dunder methods (methods with names starting with a double underscore).
Each operator has a corresponding dunder method name, so defining a method with that name tells the Python interpreter what to do when that operator is used on an instance of the class. For example, __add__() corresponds to "+," and __iadd__() corresponds to "+=." (See Special Method Names in the Python docs for more information.)
The example below shows a class (MyClass) that uses a list to store data in an attribute called list_attr. The class includes definitions of __add__() and __iadd__() that enable the use of the + and += operators to join lists, tuples, sets, and other instances of MyClass to an instance's data (list_attr).
class MyClass(object):
def __init__(self):
self.list_attr = []
def get_list_attr(self):
return self.list_attr.copy()
def __add__(self, addend):
if isinstance(addend, MyClass):
return self.list_attr + addend.list_attr
if isinstance(addend, list):
return self.list_attr + addend
if type(addend) in (tuple, set):
return self.list_attr + list(addend)
self.list_attr.append(addend)
return self.list_attr
def __iadd__(self, addend):
if isinstance(addend, MyClass):
self.list_attr.extend(addend.list_attr)
elif isinstance(addend, list):
self.list_attr.extend(addend)
elif type(addend) in (tuple, set):
self.list_attr.extend(list(addend))
else:
self.list_attr.append(addend)
return self
def __str__(self):
return f'{self.list_attr}'
if __name__ == '__main__':
my_obj = MyClass()
my_obj += [7, 8, 9]
addend = MyClass()
addend += [1, 2, 3]
a_list = ['a', 'b', 'c']
a_tuple = (3, 'd', True)
a_set = {'x', 'y', 'z'}
print(my_obj + addend) # [7, 8, 9, 1, 2, 3]
print(my_obj + a_list) # [7, 8, 9, 'a', 'b', 'c']
print(my_obj + a_tuple) # [7, 8, 9, 3, 'd', True]
print(my_obj + a_set) # [7, 8, 9, 'x', 'z', 'y']
print(my_obj + 0) # [7, 8, 9, 0]
What's your favorite "hidden" feature of your favorite programming language?
Oops! Something went wrong. Please try again later.