You can think of the Data Model as a description of Python as a framework. It formalizes the interfaces of the building blocks of the languages itself, such as sequences, iterators, functions, classes, context managers and so on.
Python allows operator overloading, which let us to define the custom behavior for a class. For instance, if a class defines a __str__()
, then it can return a readable string representation of an object.
In this post, I will enumerate some special methods that can change the behavior of an object and may putted into practice in daily work.
The special names are always spelled with leading and trailing double underscore, i.e. __str__
.
__str__
__str()__
is called by str(object)
and built-in functions such as format()
and print()
for a nicely printable string of an object. the return value must be a string
object.
The default implementation defined by the built-in type object calls __repr()__
.
class Element:
def __init__(self, k, v):
self.key = k
self.value = v
def __str__(self):
return '%s -> %s' % (self.key ,self.value)
c = Element('a', 'b')
print(c) # print a -> b
__repr__
__repr__()
is called by repr()
function to compute the string representation of an object.
The difference between __str__()
and __repr__()
is
__repr__()
is for developers, and should be unambiguous.__str__()
is for customers, and should be readable.
__hash__
__hash()
is called by built-in hash()
for operations on members of hashed collections including set
, frozenset
and dict
. __hash__()
return an integer, and the objects that are equal should have the same hash value.
A class that overrides __eq__()
and does not define __hash__()
will have its __hash__()
implicitly set to None.
__bool__
__bool__()
by the built-in bool()
is called to implement truth value testing. When this method is not defined, __len__()
is called.
class Element:
def __init__(self, k, v):
self.key = k
self.value = v
def __bool__(self):
return self.key != 0
a = Element(0, 'a')
if a: # False
print('a')
b = Element(1, 'b')
if b: # True
print('b')
__len__
__len__()
is called to implemented the build-in function len()
. It should return the length of the object.
For built-in types like
list
,str
,bytearray
etc., the interpreter takes a shortcut: the CPython implementation oflen()
actually returns the value of theob_size
field in thePyVarObject
struct that represents any variable-sized built-in object in memory.
__getitem__
__getitem__(self, key)
is called to implement evaluation of self[key]
. For sequence types, the accepted keys should be integers and slice objects.
from collections import namedtuple
Card = namedtuple('Card', ['rank','suit'])
class Deck:
ranks = [str(i) for i in range(2,11)] + list('JQKA')
suits = 'spades diamonds clubs hearts'.split()
def __init__(self):
self._cards = [Card(r, s) for s in self.suits for r in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
d = Deck()
print(len(d)) # print 52
By implementing the special methods __len__
and __getitem__
, our Deck
behaves like a standard Python sequence, allowing it to benefit from the core language features - like iteration and slicing.
d = Deck()
# [Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
print(d[:3])
for card in reversed(d):
print(card)
__missing__
__missing__(self, key)
is called by dict.__getitem__()
when key is not present.
__iadd__
__iadd__(self, other)
implements addition with assignment. For instance, for a += b
, __iadd__
might return a + b
, which would be assigned to a
.
class Element:
def __init__(self, k, v):
self.key = k
self.value = v
def __iadd__(self, other):
self.key += other.key
self.value += other.value
return self
def __str__(self):
return '%d -> %s' % (self.key, self.value)
a = Element(1, 'a')
a += Element(2, 'b')
print(a) # 3 -> ab
__abs__
The abs
built-in function returns the absolute value of integers and floats, and the magnitude of complex
number. Actually, it is __abs__
that takes effect underneath.
from math import hypot
class Vector:
def __init__(self, x=0,y=0):
self.x = x
self.y = y
def __abs__(self):
return hypot(self.x, self.y)
v = Vector(3, 4)
print(abs(v)) # 5.0
__iter__
This method is called when an iterator is required for a container. This method should return a new iterator object that can iterate over all the objects in the container.
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __iter__(self):
return (i for i in (self.x, self.y))
v = Vector(2, 3)
x, y = v
print(x, y) # 2 3
Reference