Every @property, classmethod, staticmethod, and even the invocation of regular methods under the hood works on the same mechanism: descriptors.

A descriptor is any class that implements at least one of the protocol methods: __get__, __set__, or __delete__. As soon as you bind an instance of such a class to an attribute of another class, Python intercepts access control.

1️⃣ Priority Hierarchy
When you write obj.attr, Python doesn't just look in the dictionary. It follows a strict chain:
▪️ Data Descriptor (defines __set__ or __delete__). It has absolute priority. If the attribute name is intercepted by a data descriptor, Python ignores instance.__dict__. That's why you cannot override @property with a simple assignment (unless there is a setter).
▪️ Instance dictionary (__dict__). Regular object data.
▪️ Non-Data Descriptor (defines only __get__). Regular class methods are exactly that. They sit at the bottom of the priority chain. If you manually put a function with the method name into the object's __dict__, it will override the original method.

2️⃣ Shared State Anti-pattern
A descriptor is initialized at the class level, not the instance level. It is shared among all instances. If you store data inside the descriptor instance, you will overwrite that value for all objects of your class. If Vasya changes his age, Petya will suddenly age along with him because both instances access the same memory cell of the descriptor.

How to write correctly?
State must be stored strictly in the instance's own dictionary. To avoid workarounds, Python 3.6+ introduced the magic method __set_name__. It allows the descriptor to know under which name it was created in the class, so it can legitimately store data in the __dict__ of a specific object.

class Validator:
def __set_name__(self, owner, name):
# Remember the attribute name to avoid hardcoding
self.private_name = f"_{name}"

def __get__(self, instance, owner):
if instance is None:
return self
return getattr(instance, self.private_name)

def __set__(self, instance, value):
# ✅ Write data only to the dictionary of the SPECIFIC instance
if not isinstance(value, int):
raise TypeError("Only integers allowed")
setattr(instance, self.private_name, value)

class User:
age = Validator() # age_validator.__set_name__ will get name='age'


Descriptors are a powerful tool for encapsulating logic. All the magic of frameworks like Django or SQLAlchemy is built on them ☝️

#python_anatomy