Advanced · Lesson 18 of 18 · 22 min
Internals
Descriptors, the MRO, and how attribute lookup actually works.
- Trace attribute lookup through descriptors
- Read a C3 MRO
- Use property as a descriptor you already know
Attribute lookup is a protocol
`obj.x` is not a dictionary peek. Python looks for a data descriptor on the class (something with `__get__` and `__set__` or `__delete__`), then the instance dict, then a non-data descriptor (plain functions, which is how methods bind `self`), then the class and its bases. Functions are descriptors: `C.method` is the raw function; `instance.method` is a bound method.
`property` is a data descriptor. `@property` makes a getter; `.setter` attaches a setter. That is why `self.x = 1` inside a property setter does not recurse if you store `self._x`.
class Celsius:
def __init__(self, c):
self.c = c
@property
def f(self):
return self.c * 9 / 5 + 32
@f.setter
def f(self, value):
self.c = (value - 32) * 5 / 9
t = Celsius(0)
print(t.f)
t.f = 212
print(t.c)MRO is C3 linearization
Multiple inheritance is legal. Python computes a Method Resolution Order that is consistent, monotonic, and prefers local precedence. `Class.__mro__` or `Class.mro()` shows the search order. `super()` does not mean 'my parent' — it means 'the next class in the MRO of the actual instance'. That is why cooperative `super().__init__` chains work in diamond graphs.
If you cannot explain the MRO, do not multiply inherit implementation. Mixins should be small, documented, and appear before the concrete base: `class T(JsonMixin, Base):`.
class A:
def who(self):
return "A"
class B(A):
def who(self):
return "B>" + super().who()
class C(A):
def who(self):
return "C>" + super().who()
class D(B, C):
def who(self):
return "D>" + super().who()
print(D.__mro__)
print(D().who())What CPython is
CPython objects are C structs with a type pointer and a reference count, plus a generational garbage collector for cycles. `id(x)` is typically the object's address. Interning (small ints, some strings) is an implementation detail. PyPy, GraalPy, and others exist; write to the language, not to the optimizer, until a profiler says otherwise.
You now have the spine of the language: names and objects, control, data, functions, classes, types, concurrency, and the lookup rules those rest on. The standard library is the next mountain — `pathlib`, `json`, `datetime`, `collections`, `itertools`, `functools`, `asyncio`, `unittest`/`pytest`. Read those modules the same way: objects, protocols, and a few sharp edges.
Check yourself
Quiz
1.A bound method exists because functions are:
2.super() in a method follows:
3.property is classified as:
Practice
Exercise
Write a class `Box` with a property `value`. The getter returns the stored value (default None). The setter rejects None with ValueError and otherwise stores the value. Include a method `clear()` that sets the stored value back to None without going through the setter.
Tutor
Ask a question about this lesson. Answers are generated on demand and capped so the session stays focused.