Skip to content

Data Model

The Python data model is what makes Python feel like Python. Method binding, attribute access, operator behavior, and many framework-level tricks are all expressions of the same object protocol.

Quick takeaway: a lot of Python syntax is just a readable front-end for object protocol methods. `len(obj)`, `obj[x]`, `obj.attr`, and bound methods all come from the data model.

See the Model First

Python syntax maps to object protocol behavior through type objects, slots, and dunder methods.

Why It Matters

  • It explains why functions turn into bound methods on instances.
  • It makes attribute lookup and descriptors feel mechanical instead of magical.
  • It gives you the right base layer for reading framework internals.

Separate Identity, Type, and Value

  • identity: whether this is the same object
  • type: which behaviors and rules are attached
  • value: the object's current state

Python draws a strong line between them. Mutable objects can keep the same identity while their value changes.

Why Functions Become Methods

py
class User:
    def greet(self) -> str:
        return f"hello from {self.__class__.__name__}"


user = User()
print("class attribute:", User.greet)
print("instance attribute:", user.greet)
print("call result:", user.greet())

The function stored on the class is transformed into a bound method when accessed through an instance. That binding behavior is part of the data model.

Attribute Lookup Gets Interesting Fast

py
class Profile:
    def __init__(self) -> None:
        self.name = "jae"

    def __getattribute__(self, name: str) -> object:
        print("getattribute:", name)
        return super().__getattribute__(name)

    def __getattr__(self, name: str) -> str:
        return f"<missing {name}>"


profile = Profile()
print(profile.name)
print(profile.nickname)

`__getattribute__` runs for almost every attribute access. `__getattr__` is only a fallback after normal lookup fails.

Checklist

Why not `obj.len()`?

Python syntax is attached to protocol hooks and slots, not to arbitrary instance method naming.

Why do methods bind?

Functions on classes behave like descriptors and participate in binding.

Why do frameworks feel magical?

Because class bodies, descriptors, metaclasses, and annotations all build on this model.

When do you override `__getattribute__`?

Only when you need global interception. Descriptors and properties are usually the safer tool.

Official Sources

Built with VitePress for a Python 3.14 handbook.