Python › Descriptors & Properties

What is the difference between a data descriptor and a non-data descriptor, and how does this affect attribute lookup priority?

Intermediate

Answer

A DATA descriptor implements BOTH __get__ AND __set__ (or __delete__); a NON-DATA descriptor implements ONLY __get__. Data descriptors take priority OVER an instance's __dict__ during attribute lookup, while non-data descriptors are OVERRIDDEN by an instance's __dict__ if the same name exists there — this priority difference is exactly how @property (a data descriptor) always wins even if you try to shadow it in self.__dict__.
class NonDataDescriptor:
    def __get__(self, instance, owner):
        return "from descriptor"

class Example:
    attr = NonDataDescriptor()

e = Example()
e.__dict__["attr"] = "from instance dict"
print(e.attr)  # 'from instance dict' -- instance __dict__ wins for non-data descriptors
Real-world example Understanding why a @property (data descriptor) can't be silently overridden by setting self.__dict__['prop_name'] directly.

Common follow-ups: Why does a functions themselves (used for bound methods) behave as a non-data descriptor?