Skip to main content

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)​

CallDunderAudience
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​

Quiz

Object representations

1 / 2

print(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​

Quiz

classmethod

1 / 2

The 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​

Quiz

Hash and privacy

1 / 2

A 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​

Key takeaways
  1. __repr__ is for developers, __str__ for users, __format__ for specs, __bytes__ for binary.
  2. __iter__ unlocks unpacking and tuple(v).
  3. @classmethod is the usual alternate constructor; staticmethod is rarely better than a module function.
  4. Hashable objects need stable __hash__/__eq__ and typically read-only data.
  5. __slots__ trades flexibility for memory; _name / __name are conventions, not access control.

Exercises​

Activity: Magnitude

Vec with ints x, y. __abs__ returns integer round(hypot) — for (3,4) that is 5. Print abs(v).