A descriptor is an object defining get/set/delete and intercepting attribute access
A descriptor is any object that defines get, and optionally set or delete. When stored as a class attribute, Python calls it instead of returning the object itself when the attribute is accessed. Data descriptors define set or delete and take precedence over the instance dict; non-data descriptors define only get and can be shadowed by an instance attribute. This is the mechanism behind functions becoming bound methods, property, classmethod, staticmethod, and slot. ORMs like Django use descriptors to map Python attributes to database columns: accessing a field on an instance returns the loaded value, and assigning to it marks the field dirty for later flush.
data descriptor: get plus set or delete; instance dict cannot shadow it.
non-data descriptor: get only; instance attributes can shadow it, which is why overriding a method by setting an instance attribute works.
Functions are non-data descriptors, which is exactly how method binding is implemented.
set_name lets a descriptor learn its attribute name at class creation time, useful for ORMs.
Trade-off: descriptors are powerful but subtle. A plain property covers most cases; reach for a custom descriptor only when you need reuse across many attributes.
Common mistake: forgetting that descriptor get is called with None as the instance when accessed on the class.
Version note: set_name was added in 3.6. The overall descriptor protocol has been stable since Python 2.2.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience