Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

Python Special Methods and Object Behavior

Learn how Python classes customize built-in operations with special methods such as __repr__, __str__, __format__, __len__, __bool__, __contains__, __eq__, and __add__.

Levels: Intermediate ~24 min read

Intermediate

Python lets a class participate in built-in operations by defining methods with special names. You do not normally call these methods yourself; Python calls the appropriate hook when code uses syntax such as str(obj), len(obj), obj == other, or obj + other.

Special methods are language hooks

A special method is a method whose name follows Python's double-underscore convention and whose meaning is defined by the language. The useful mental model is simple: your class supplies a hook, and ordinary Python syntax triggers that hook.

python
1
2
3
4
5
6
7
8
9
class Ticket:
    def __init__(self, code):
        self.code = code

    def __repr__(self):
        return f"Ticket({self.code!r})"

ticket = Ticket(42)
print(repr(ticket))

The ordinary repr() call triggers the class's __repr__ hook.

Choose a useful __repr__

__repr__ defines the representation returned by repr(). The official data model describes it as the object's official representation and recommends making it information-rich and unambiguous. It should return a string. A common goal is to make the result useful while debugging.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class User:
    def __init__(self, name, active):
        self.name = name
        self.active = active

    def __repr__(self):
        return f"User(name={self.name!r}, active={self.active!r})"

user = User("Maya", True)
print(repr(user))

repr() can expose enough state to make a value easy to inspect while debugging.

Make printing readable with __str__

__str__ defines the informal string representation used by str() and print(). It is usually more convenient or concise than __repr__. If a class has no __str__, Python can fall back to __repr__ for the informal representation.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __str__(self):
        return f"{self.name}: {self.price:.2f}"

item = Product("Notebook", 12.5)
print(item)
print(str(item))

print() and str() use the object's __str__ method for a user-facing representation.

Customize formatted output with __format__

__format__ defines how format(obj, format_spec), formatted string literals, and str.format() produce a formatted string for an object. The format specification is a string; the class decides how to interpret it. Keep this hook distinct from __str__, which supplies the ordinary informal representation. A custom __format__ method must return a string.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Price:
    def __init__(self, amount):
        self.amount = amount

    def __format__(self, format_spec):
        if format_spec == "currency":
            return f"${self.amount:.2f}"
        return format(self.amount, format_spec)

price = Price(12.5)
print(f"{price:currency}")
print(format(price, ".1f"))

The class handles its own currency format and delegates ordinary numeric format specifications to the stored float.

The default __format__ implementation inherited from object accepts an empty format specification and uses __str__(); it raises TypeError for a non-empty specification. Defining __format__ makes your class responsible for handling the formats it chooses to support. The Format Specification lesson remains the owner of the standard format-specification mini-language.

Customize binary conversion with __bytes__

__bytes__() defines the binary representation returned by bytes(obj). The method must return a bytes object; it is not a replacement for text encoding, which should specify an encoding explicitly. Choose and document a stable byte layout for your class, and keep its values within the byte range when building the result from integers.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class StatusPacket:
    def __init__(self, status, value):
        self.status = status
        self.value = value

    def __bytes__(self):
        return bytes((self.status, self.value))

packet = StatusPacket(7, 3)
print(bytes(packet))
print(list(bytes(packet)))

The class defines a two-byte representation for its status and value fields. The bytes constructor calls __bytes__() and receives bytes, not text.

Do not confuse bytes(obj) with encoding a string: str.encode() converts text using a chosen character encoding, while __bytes__() lets a class define its own binary representation. The method must return bytes; returning str, bytearray, or another type raises TypeError.

Quick check

Which expression asks the custom object for its binary representation?

class Packet:
    def __bytes__(self):
        return b"\x07\x03"

    def __str__(self):
        return "packet text"

    def __repr__(self):
        return "Packet()"

packet = Packet()
print()

Give objects a length with __len__

__len__ defines the result of len(obj). The method must return a non-negative integer. Once a class provides it, the object can participate naturally in APIs that ask for a length.

python
1
2
3
4
5
6
7
8
9
class Playlist:
    def __init__(self, songs):
        self.songs = songs

    def __len__(self):
        return len(self.songs)

playlist = Playlist(["Intro", "Theme", "Finale"])
print(len(playlist))

Defining __len__ makes len(playlist) delegate to the class's length hook.

Control truth testing with __bool__

__bool__ defines how an object behaves in a Boolean context and when passed to bool(). It must return True or False. If __bool__ is absent, Python can use __len__ instead: a zero length is false and a nonzero length is true.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Account:
    def __init__(self, balance):
        self.balance = balance

    def __bool__(self):
        return self.balance > 0

empty = Account(0)
funded = Account(25)
print(bool(empty))
print(bool(funded))
print(bool(funded and True))

A class can define its own truth rule instead of relying on object length.

Customize membership with __contains__

__contains__ controls membership tests such as item in obj. It lets a class define what membership means for itself. This is different from the iterator protocol taught in the Iterators lesson, which can provide a fallback when __contains__ is not defined.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Catalog:
    def __init__(self, names):
        self.names = set(names)

    def __contains__(self, name):
        return name in self.names

catalog = Catalog(["Python", "HTML", "CSS"])
print("Python" in catalog)
print("Git" in catalog)

The in operator calls the class's membership hook when __contains__ is defined.

Customize equality with __eq__

__eq__ defines the behavior of ==. A simple implementation can compare the relevant attributes and return a Boolean. For more general classes, a comparison method can return NotImplemented when it does not support the other operand; Python can then try the appropriate fallback.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return self.x == other.x and self.y == other.y

a = Point(2, 3)
b = Point(2, 3)
c = Point(4, 1)
print(a == b)
print(a == c)

The == operator can compare object values instead of default identity-based equality.

Customize + with __add__

__add__ defines the behavior of the + operator when your object is on the left. The method receives the other operand and should return the result, or NotImplemented when the operation is not supported for that operand type.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Money:
    def __init__(self, amount):
        self.amount = amount

    def __add__(self, other):
        if not isinstance(other, Money):
            return NotImplemented
        return Money(self.amount + other.amount)

    def __repr__(self):
        return f"Money({self.amount})"

print(Money(10) + Money(7))

Defining __add__ lets ordinary + syntax create a result appropriate for the class.

Special methods work together

Real classes often define several special methods that describe different aspects of the same object. Keep each method focused on the operation it customizes. You do not need to implement a large collection of hooks: add the ones that make the class behave naturally.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Basket:
    def __init__(self, items):
        self.items = list(items)

    def __len__(self):
        return len(self.items)

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

    def __contains__(self, item):
        return item in self.items

    def __repr__(self):
        return f"Basket({self.items!r})"

basket = Basket(["bread", "milk"])
print(len(basket))
print(bool(basket))
print("milk" in basket)
print(repr(basket))

Several hooks can describe one object naturally without changing how callers use ordinary Python syntax.

Keep advanced hooks separate

Python has many more special methods for attribute access, descriptors, class creation, numeric operations, subscriptions, pattern matching, and other protocols. This lesson establishes the core idea and the most useful object-facing hooks. The existing Iterators lesson remains the owner of __iter__() and __next__(); advanced protocols deserve separate lessons when they are justified.

Was this page helpful?

Mark this learning resource complete to track your learning progress.