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.
Top comments (0)