A Pythonic Object
Source: Fluent Python, 2nd Edition — Chapter 11 by Luciano Ramalho
A type feels Pythonic when it plays with the language the way list and float do: repr, unpacking, abs, format, hashing — without forcing users through a private API. You get that from the data model, not from inheriting a magic base class. Implement the dunders you need; skip the rest on ordinary app classes. Libraries get held to a higher bar.
Chapter 1 used a vector as a teaser. Here we grow a 2-D vector into something you could publish: constructors, displays, read-only components, hashability, and a note on __slots__ and “private” names.
Two string faces (and friends)
| Call | Dunder | Audience |
|---|---|---|
repr(obj) | __repr__ | You, in a debugger. Ideally eval-able. |
str(obj) / print | __str__ | Humans. Falls back to __repr__ if missing. |
bytes(obj) | __bytes__ | Binary dump. |
format(obj, spec) / f-strings | __format__ | Mini-language (:.2f, custom codes). |
In Python 3, __repr__, __str__, and __format__ return str. Only __bytes__ returns bytes.
A small vector usually also wants __iter__ (so x, y = v), __eq__, __abs__ (magnitude), __bool__ (false when length is 0).
def __repr__(self):
cls = type(self).__name__
return f"{cls}({self.x!r}, {self.y!r})"
def __iter__(self):
yield self.x
yield self.y
*self in format works once the instance is iterable. __eq__ via tuple(self) == tuple(other) is handy for tests and also true for [3, 4] — a feature or a trap; operator chapter later.
Convert x and y to float in __init__ so bad inputs fail immediately.
🧪 repr, str, unpack, abs
Mini quiz — representations
Object representations
1 / 2print(v) uses
Pick an answer to see if you are right.
Challenge: iterable point
Pt stores two ints x and y. Implement __iter__ so a, b = Pt(...) works, and __repr__ so it prints exactly Pt(X, Y) with the numbers.
classmethod vs staticmethod
@classmethod methods receive the class (cls) as the first argument. Use them for alternate constructors so subclasses get the right type:
@classmethod
def from_pair(cls, pair):
x, y = pair
return cls(x, y)
@staticmethod receives no automatic first argument. It is a function parked in the class body. If it never touches the class, a module-level function is usually clearer.
🧪 cls vs no special arg
Mini quiz — classmethod
classmethod
1 / 2The first argument to a classmethod is conventionally
Pick an answer to see if you are right.
Challenge: alternate constructor
Pt.from_csv(line) is a classmethod. The line is x,y (two ints). It must return a Pt. Pt(1, 2) still works.
__format__ and f-strings
f"{v:.2f}" and format(v, ".2f") call v.__format__(".2f"). If you ignore unknown specs, delegate to format(float, spec). Custom codes (for example polar vs cartesian) are allowed if you document them. f-strings did not change the mini-language; an old __format__ still works.
Read-only fields, hash, slots, “private”
To put a vector in a set or use it as a dict key it must be hashable: __hash__ returns an int that never changes for that value, and __eq__ agrees. That means mutating x after insert is cheating. The usual pattern: store _x, expose x as a @property with no setter.
def __hash__(self):
return hash((self.x, self.y))
__slots__ = ("_x", "_y") skips per-instance __dict__ (saves memory, forbids surprise attributes). Do not add __dict__ unless you need it. Subclassing slotted classes has extra rules — use slots when you really have many instances.
Python has no true private fields. _x is a convention (internal). __x inside class Vec is name-mangled to _Vec__x, which only slows accidental access, not determined users.
🧪 property and hash
Mini quiz — hash and privacy
Hash and privacy
1 / 2A hashable instance should not
Pick an answer to see if you are right.
Challenge: hashable pair
Pair has read-only a and b (ints). Implement __eq__ and __hash__ so a set of two equal pairs has length 1. Do not allow p.a = ....
Chapter summary
__repr__is for developers,__str__for users,__format__for specs,__bytes__for binary.__iter__unlocks unpacking andtuple(v).@classmethodis the usual alternate constructor;staticmethodis rarely better than a module function.- Hashable objects need stable
__hash__/__eq__and typically read-only data. __slots__trades flexibility for memory;_name/__nameare conventions, not access control.
Exercises
Vec with ints x, y. __abs__ returns integer round(hypot) — for (3,4) that is 5. Print abs(v).