Shaikhul Islam
Posted on February 17, 2019
Python's double underscore (aka dunder) methods (ex. __len__
) aren't magic method but more than that. Each of python's built-in functions has a corresponding dunder method. Officially they are documented under python data model. Try google python data model
, first result would point to python docs
So whats the big deal? Ever wonder why python's len
method accept almost anything?
>>> len(range(5))
5
Now imagine a simple class like this
>>> class Foo:
... pass
...
What would it return if I provide a Foo
instance to len
method?
>>> f = Foo()
>>> len(f)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'Foo' has no len()
It throws an exception TypeError
complaining Foo
has no len
. Right, if we implement a __len__
on Foo
object and return anything, python's len
will look for a corresponding __len__
method for that object.
>>> class Foo:
... def __len__(self):
... return 5
...
>>> f = Foo()
>>> len(f)
5
boom! now it works and return what we want it to return by implementing the __len__
method.
Other most common dunder methods are
String representation
In [11]: class Point:
...: def __init__(self, x, y):
...: self.x = x
...: self.y = y
...:
...: def __str__(self):
...: return 'Stringified Point: ({x}, {y})'.format(x=self.x, y=self.y)
...:
...: def __repr__(self):
...: return 'Representing Point: ({x}, {y})'.format(x=self.x, y=self.y)
...:
In [12]: p = Point(2,3)
In [13]: p
Out[13]: Representing Point: (2, 3)
In [14]: str(p)
Out[14]: 'Stringified Point: (2, 3)'
Operator overloading
We can implement binary arithmetic operations (ex +
, -
etc) implementing specific dunder methods.
In [21]: class Point:
...: # continuing from previous example
...: def __add__(self, other):
...: return Point(self.x + other.x, self.y + other.y)
...:
In [22]: p1 = Point(1, 2)
In [23]: p2 = Point(3,5)
In [26]: p3
Out[26]: Representing Point: (4, 7)
To learn more about emulating numeric types checkout the doc
Object comparison
We can implement __eq__
to compare between objects.
In [36]: class Point:
...: # continuing from previous example
...: def __eq__(self, other):
...: # custom comparison
...: return self.x == other.x and self.y == other.y
...:
In [37]: p1 = Point(2, 3)
In [38]: p2 = Point(2, 3)
In [39]: p1 == p2
Out[39]: True
In [40]: p3 = Point(2, 4)
In [41]: p1 == p3
Out[41]: False
To check other object customization please check the doc
Object attribute access
In [44]: class Point:
...: # continuing from previous example
...: def __getattr__(self, attr):
...: if (attr == 'X'):
...: return self.x
...: elif (attr == 'Y'):
...: return self.y
...: else:
...: raise AttributeError('Point object has no attribute {attr}'.format(attr=attr))
...:
In [45]: p = Point(2, 3)
In [46]: p.x
Out[46]: 2
In [47]: p.X
Out[47]: 2
In [48]: p.Xx
--------------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-48-9a49f22e192d> in <module>
---------> 1 p.Xx
<ipython-input-44-f5f17ce1e8d6> in __getattr__(self, attr)
23 return self.y
24 else:
--------> 25 raise AttributeError('Point object has no attribute {attr}'.format(attr=attr))
26
AttributeError: Point object has no attribute Xx
More attribute access can be found in the doc
Emulating container/collection
We can also treat objects as collection and do all sort of function call that accept a collection.
In [49]: class Point:
...: # continuing from previous example
...: def __getitem__(self, index):
...: if index == 0:
...: return self.x
...: elif index == 1:
...: return self.y
...: else:
...: raise IndexError('No item found with index {index}'.format(index=index))
...:
In [50]: p = Point(2, 3)
In [51]: p[0]
Out[51]: 2
In [53]: p[1]
Out[53]: 3
In [54]: p[2]
--------------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-54-21c545b39f61> in <module>
---------> 1 p[2]
<ipython-input-49-6dd2485f38f5> in __getitem__(self, index)
31 return self.y
32 else:
--------> 33 raise IndexError('No item found with index {index}'.format(index=index))
34
IndexError: No item found with index 2
Checkout the doc for whole list of dunder methods to emulate collection.
The best thing of python data model is that programmers hardly need to call the dunder method (except __init__
and few other customization methods), python interpreter will call these methods as needed.
I have put the complete Point
class in this gist, feel free to play with all other dunder methods and share what you have learned with us.
Posted on February 17, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.