Data Model
Special Methods
Start with a normal class that stores its data. Special methods build on ordinary instance state.
Source
class Bag:
def __init__(self, items):
self.items = list(items)
bag = Bag(["a", "b"])
print(bag.items)Output
['a', 'b']Implement __len__ to let len() ask the object for its size using Python's standard protocol.
Source
class Bag:
def __init__(self, items):
self.items = list(items)
def __len__(self):
return len(self.items)
bag = Bag(["a", "b"])
print(len(bag))Output
2Implement __iter__ to make the object iterable. Then tools such as list() can consume it without a custom method name.
Source
class Bag:
def __init__(self, items):
self.items = list(items)
def __len__(self):
return len(self.items)
def __iter__(self):
return iter(self.items)
bag = Bag(["a", "b"])
print(list(bag))Output
['a', 'b']Implement __repr__ to give the object a useful developer-facing representation when it is printed or inspected. With no __str__ defined, print() falls back to __repr__.
Source
class Bag:
def __init__(self, items):
self.items = list(items)
def __len__(self):
return len(self.items)
def __iter__(self):
return iter(self.items)
def __repr__(self):
return f"Bag({self.items!r})"
bag = Bag(["a", "b"])
print(bag)Output
Bag(['a', 'b'])Add __str__ for an end-user representation. print() and str() prefer __str__; repr() and the REPL still use __repr__. Keep __repr__ unambiguous for debugging and let __str__ be the friendly form.
Source
class Bag:
def __init__(self, items):
self.items = list(items)
def __repr__(self):
return f"Bag({self.items!r})"
def __str__(self):
return ", ".join(self.items)
bag = Bag(["a", "b"])
print(bag)
print(repr(bag))Output
a, b
Bag(['a', 'b'])__eq__ decides what equality means for the type. Defining __eq__ removes the default __hash__, so add __hash__ back when instances should work in sets or as dict keys. __lt__ enables < and, with the rest of the order family, sorted().
Source
class Bag:
def __init__(self, items):
self.items = list(items)
def __eq__(self, other):
return isinstance(other, Bag) and self.items == other.items
def __hash__(self):
return hash(tuple(self.items))
def __lt__(self, other):
return len(self.items) < len(other.items)
print(Bag(["a", "b"]) == Bag(["a", "b"]))
print(Bag(["a"]) < Bag(["a", "b"]))
print(hash(Bag(["a"])) == hash(Bag(["a"])))Output
True
True
TrueThe container protocols make instances behave like built-in containers. __contains__ powers in, __getitem__/__setitem__ power subscription, and __bool__ decides truthiness for if and while. See [container-protocols](/data-model/container-protocols) for the full surface.
Source
class Bag:
def __init__(self, items):
self.items = list(items)
def __contains__(self, item):
return item in self.items
def __getitem__(self, index):
return self.items[index]
def __setitem__(self, index, value):
self.items[index] = value
def __bool__(self):
return bool(self.items)
bag = Bag(["a", "b"])
print("a" in bag)
print(bag[0])
bag[1] = "z"
print(bag.items)
print(bool(Bag([])))Output
True
a
['a', 'z']
False__call__ makes an instance callable like a function — useful for stateful operations whose configuration deserves a name. __enter__ and __exit__ make a class a context manager so it can be used with with. The focused [callable-objects](/data-model/callable-objects) and [context-managers](/data-model/context-managers) pages go deeper.
Source
class Multiplier:
def __init__(self, factor):
self.factor = factor
def __call__(self, value):
return value * self.factor
triple = Multiplier(3)
print(triple(5))
class Trace:
def __enter__(self):
print("enter")
return self
def __exit__(self, *exc):
print("exit")
return False
with Trace():
print("inside")Output
15
enter
inside
exitNotes
- Dunder methods are looked up by Python's data model protocols.
__repr__is the developer-facing form;__str__is the user-facing form.print()falls back to__repr__when__str__is missing.- Defining
__eq__removes the default__hash__; restore it when the type should be hashable. - Container protocols (
__contains__,__getitem__,__setitem__,__bool__) make instances behave like built-in containers. __call__makes instances callable;__enter__/__exit__make them context managers.- Implement the smallest protocol that makes your object feel native.
See also
- related: Container Protocols
- related: Operator Overloading
- related: Callable Objects
- related: Context Managers
Run the complete example
Expected output
2
['a', 'b']
a, b
Bag(['a', 'b'])
True
True
True
True
a
['a', 'z']
False
15
enter
inside
exit
Execution time appears here after you run the example.