#python

Python Attribute Lookup

Python Attribute Lookup

In an effort to understand the very basics, I’ve been implementing classes in Python from scratch, starting with the functions and dictionaries. I was curious how to implement attribute lookup how python actually does it. So I went down a rabbit hole and came across this article that covers it exhaustively. Learned a great deal about how alot of the higher level ORMS, frameworks like FastAPI and dataclasses are implemented.

Some key takeaways are:

You can also have things called descriptors. These are basically just objects that change how you access the different object attributes. There’s two:

Data descriptors have __set__, __delete__ and __get__ methods implemented while non-data descriptors only have __get__ implemented. The easiest example of this is the @property object. Under the hood, it’s basically a data descriptor.

The data descriptor is the first thing that Python looks up when trying to get an attribute. These property descriptors also override an instance’s attributes, non-data descriptors, and plain class attributes.

To illustrate this, here’s an example:

class Foo:
    @property
    def x(self):
        return "computed"

obj = Foo()
obj.__dict__["x"] = "shadow?"
obj.x

This will actually return computed and not shadow because during @property is a data descriptor, thus Python will look at it’s __set__ or __get__ methods first which x is. Thus it will return before we saw the instance obj attribute shadow.

So the general approach is:

Borrowing an image and example from the above article:

class A(object): pass

class B(object):

    x = x from B

class C(A, B):pass

class D(B):

    x = x from D
    
class E(C, D): pass

![[Pasted image 20260301141019.png]]

>>> E.__mro__
(__main__.E, __main__.C, __main__.A, __main__.D, __main

C3 linearization produces an MRO that satisfies three constraints:

1. Local precedence order

If a class lists bases as class C(A, B), then A must appear before B in the MRO of C.

2. Monotonicity

A subclass’s MRO must preserve the order of its parents’ MROs. This prevents “jumps” that would break inheritance consistency.

3. No contradictions

If two parents disagree on ordering, Python must find a consistent merge or raise an error.

This has made it so much easier to understand what frameworks like FastAPI and ORMs do behind the scenes.

#python #classes