The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Define developer representationdef __repr__(self): return f'Type({self.value!r})'View examples
Define readable textdef __str__(self): return self.labelView examples
Define truth testingdef __bool__(self): return self.is_readyView examples
Define equalitydef __eq__(self, other): return self.value == other.value if isinstance(other, Type) else NotImplementedView examples
Define hashingdef __hash__(self): return hash((self.first, self.second))View examples
Decline an operationreturn NotImplementedView examples
Define less-than orderingdef __lt__(self, other): return self.value < other.value if isinstance(other, Type) else NotImplementedView examples
Define additiondef __add__(self, other): return type(self)(self.value + other.value)View examples
Define reflected additiondef __radd__(self, other): return type(self)(other + self.value)View examples
Report container lengthdef __len__(self): return len(self._items)View examples
Support indexed accessdef __getitem__(self, key): return self._items[key]View examples
Optimize membershipdef __contains__(self, item): return item in self._itemsView examples
Provide iterationdef __iter__(self): return iter(self._items)View examples
Manage one attributevalue = property(getter, setter)View examples
Handle missing attributesdef __getattr__(self, name): return self.fields[name]View examples
Intercept all instance lookupdef __getattribute__(self, name): return object.__getattribute__(self, name)View examples
Define descriptor readingdef __get__(self, instance, owner=None): return self if instance is None else instance.__dict__[self.name]View examples
Learn the assigned namedef __set_name__(self, owner, name): self.name = nameView examples

Special methods let user-defined classes participate in Python syntax and built-in protocols. Implement the smallest coherent protocol your type actually supports, preserve invariants such as equal objects having equal hashes, and return NotImplemented when another operand deserves a chance. Python usually looks up implicit special methods on the type, so changing them on one instance is not a reliable customization technique.

Step by step

Detailed examples

01

Separate developer representation, display text, and truth

repr should help identify or reconstruct an object when practical, while str targets readable display and falls back to repr when absent. Containers display their elements with repr. Truth testing calls __bool__, then falls back to nonzero __len__, and otherwise treats instances as true. Keep these methods inexpensive and free from consequential side effects.

Customize three distinct object views
class Batch:
    def __init__(self, name, jobs):
        self.name = name
        self.jobs = list(jobs)

    def __repr__(self):
        return f'Batch({self.name!r}, {self.jobs!r})'

    def __str__(self):
        return f'{self.name}: {len(self.jobs)} jobs'

    def __bool__(self):
        return bool(self.jobs)

batch = Batch('nightly', ['build', 'test'])
print(repr(batch))
print(batch)
print(bool(batch), bool(Batch('empty', [])))
Output
Batch('nightly', ['build', 'test'])
nightly: 2 jobs
True False
Back to quick reference ↑
02

Keep equality and hashing consistent

Equality should compare the semantic state that defines a value and return NotImplemented for unrelated types rather than raising or returning an arbitrary false result. Objects that compare equal must have equal hashes. Mutable value objects normally remain unhashable because changing key fields after insertion corrupts dictionary and set assumptions; defining __eq__ without an appropriate __hash__ makes instances unhashable automatically.

Create an immutable value suitable for dictionary keys
class Coordinate:
    __slots__ = ('_x', '_y')
    def __init__(self, x, y):
        object.__setattr__(self, '_x', x)
        object.__setattr__(self, '_y', y)
    def __setattr__(self, name, value):
        raise AttributeError('Coordinate is immutable')
    def __eq__(self, other):
        if not isinstance(other, Coordinate):
            return NotImplemented
        return (self._x, self._y) == (other._x, other._y)
    def __hash__(self):
        return hash((self._x, self._y))

first = Coordinate(2, 5)
second = Coordinate(2, 5)
lookup = {first: 'station'}
print(first == second)
print(lookup[second])
print(first == (2, 5))
Output
True
station
False
Back to quick reference ↑
03

Return values or NotImplemented from operators

Rich comparisons and arithmetic use a negotiation between operands. Return NotImplemented when the operand type is unsupported so Python can try its reflected method; raising NotImplemented is an error because it is a singleton value, not an exception. Ordering need not follow equality automatically. Define a clear domain order, or use functools.total_ordering when its convenience outweighs slower, deeper generated comparisons.

Implement ordering and reflected addition
from functools import total_ordering

@total_ordering
class Score:
    def __init__(self, points): self.points = points
    def __eq__(self, other):
        return self.points == other.points if isinstance(other, Score) else NotImplemented
    def __lt__(self, other):
        return self.points < other.points if isinstance(other, Score) else NotImplemented
    def __add__(self, other):
        return Score(self.points + other.points) if isinstance(other, Score) else NotImplemented
    def __radd__(self, other):
        return Score(other + self.points) if isinstance(other, int) else NotImplemented
    def __repr__(self): return f'Score({self.points})'

print(sorted([Score(8), Score(3), Score(5)]))
print(Score(2) + Score(4))
print(10 + Score(5))
Output
[Score(3), Score(5), Score(8)]
Score(6)
Score(15)
Back to quick reference ↑
04

Implement only the container capabilities you promise

__len__, __iter__, __contains__, and __getitem__ are separate capabilities even though Python has fallbacks between some of them. Explicit iteration and membership make behavior and performance clearer. __getitem__ receives slice objects as well as scalar keys, so delegating to an underlying sequence conveniently preserves both. Mutation methods should be omitted when the abstraction is read-only.

Expose a read-only sequence view
class Window:
    def __init__(self, values): self._values = tuple(values)
    def __len__(self): return len(self._values)
    def __iter__(self): return iter(self._values)
    def __getitem__(self, key): return self._values[key]
    def __contains__(self, value): return value in self._values

window = Window([4, 6, 8, 10])
print(len(window), 6 in window)
print(list(window))
print(window[1:3])
Output
4 True
[4, 6, 8, 10]
(6, 8)
Back to quick reference ↑
05

Use properties for focused attribute invariants

property is a descriptor that keeps familiar attribute syntax while executing getter, setter, or deleter logic. It is appropriate for validation or a computed compatibility layer, but surprising I/O behind ordinary access is difficult to reason about. Store the underlying state under a different name to avoid recursively invoking the property.

Validate assignments through a property
class Percentage:
    def __init__(self, value):
        self.value = value

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, value):
        if not 0 <= value <= 100:
            raise ValueError('outside 0..100')
        self._value = value

progress = Percentage(40)
progress.value += 15
print(progress.value)
try:
    progress.value = 120
except ValueError as error:
    print(error)
Output
55
outside 0..100
Back to quick reference ↑
06

Prefer __getattr__ for narrow lookup fallback

__getattr__ runs only when normal lookup raises AttributeError, making it safer for computed aliases or delegation. __getattribute__ intercepts every instance lookup and must call object.__getattribute__ or another base implementation to avoid infinite recursion. Missing names must ultimately raise AttributeError so hasattr, getattr defaults, introspection, and debugging retain their expected semantics.

Provide aliases without masking real attributes
class Record:
    def __init__(self, **fields):
        self.fields = fields

    def __getattr__(self, name):
        try:
            return self.fields[name]
        except KeyError:
            raise AttributeError(name) from None

record = Record(owner='Ada', status='ready')
print(record.owner)
print(record.fields['status'])
print(getattr(record, 'missing', 'unknown'))
print(hasattr(record, 'missing'))
Output
Ada
ready
unknown
False
Back to quick reference ↑
07

Reuse managed fields with descriptors

A descriptor defines __get__, __set__, or __delete__ and controls attribute access when stored on a class. A descriptor defining __set__ is a data descriptor and takes precedence over an instance dictionary entry. __set_name__, called during class creation, lets one descriptor discover its storage name; return self when __get__ receives instance=None so class-level inspection remains useful.

Validate multiple named fields with one descriptor
class Positive:
    def __set_name__(self, owner, name):
        self.storage_name = '_' + name
    def __get__(self, instance, owner=None):
        if instance is None:
            return self
        return getattr(instance, self.storage_name)
    def __set__(self, instance, value):
        if value <= 0:
            raise ValueError('must be positive')
        setattr(instance, self.storage_name, value)

class Rectangle:
    width = Positive()
    height = Positive()
    def __init__(self, width, height):
        self.width, self.height = width, height

shape = Rectangle(3, 4)
print(shape.width * shape.height)
print(Rectangle.width.storage_name)
Output
12
_width
Back to quick reference ↑

Local code tester

Build a protocol-aware value collection

Experiment with representation, equality, iteration, membership, slicing, and truth testing on one small class.

Runs in your browser
Output
Press Run to load Python locally.

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. Python Software FoundationData model — Special method namesdocs.python.org
  2. Python Software FoundationDescriptor Guidedocs.python.org
  3. Python Software FoundationBuilt-in Functionsdocs.python.org
  4. Python Software Foundationcollections.abc — Abstract Base Classes for Containersdocs.python.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback