0
0
PythonComparisonIntermediate · 4 min read

Data Descriptor vs Non Data Descriptor in Python: Key Differences

In Python, a data descriptor defines both __get__ and __set__ methods, controlling attribute access and assignment, while a non-data descriptor defines only __get__, allowing attribute assignment to override it. Data descriptors have higher priority in attribute lookup than instance variables, unlike non-data descriptors.
⚖️

Quick Comparison

This table summarizes the main differences between data descriptors and non-data descriptors in Python.

AspectData DescriptorNon-Data Descriptor
Methods Implemented__get__ and __set__ (or __delete__)Only __get__
Attribute ControlControls both access and assignmentControls only access
Priority in LookupOverrides instance variablesCan be overridden by instance variables
Use CaseFor managed attributes needing validation or computed valuesFor read-only or computed attributes
ExampleProperty with setterMethod or cached property without setter
⚖️

Key Differences

A data descriptor in Python is a class that implements both __get__ and __set__ methods (or __delete__). This means it can control what happens when you read, write, or delete an attribute. Because of this, data descriptors take priority over instance variables during attribute lookup. This makes them ideal for managing attributes that require validation, computed values, or other side effects when changed.

On the other hand, a non-data descriptor only implements __get__. It controls what happens when you access an attribute but does not control assignment. If you assign a value to the attribute on the instance, it will override the descriptor. This behavior is common for methods or read-only computed properties.

In summary, the main difference lies in whether the descriptor controls attribute assignment (data descriptor) or only attribute access (non-data descriptor). This affects how Python looks up attributes and whether instance variables can override the descriptor.

⚖️

Code Comparison

Here is an example of a data descriptor that manages an attribute with validation on assignment.

python
class DataDescriptor:
    def __init__(self):
        self.value = None

    def __get__(self, instance, owner):
        print("DataDescriptor __get__ called")
        return self.value

    def __set__(self, instance, value):
        print("DataDescriptor __set__ called")
        if not isinstance(value, int):
            raise ValueError("Value must be an integer")
        self.value = value

class MyClass:
    attr = DataDescriptor()

obj = MyClass()
obj.attr = 10  # Calls __set__
print(obj.attr)  # Calls __get__

# obj.attr = 'hello'  # Would raise ValueError
Output
DataDescriptor __set__ called DataDescriptor __get__ called 10
↔️

Non-Data Descriptor Equivalent

This example shows a non-data descriptor that only implements __get__. Assignment to the attribute on the instance overrides the descriptor.

python
class NonDataDescriptor:
    def __get__(self, instance, owner):
        print("NonDataDescriptor __get__ called")
        return 42

class MyClass:
    attr = NonDataDescriptor()

obj = MyClass()
print(obj.attr)  # Calls __get__
obj.attr = 100  # Overrides descriptor on instance
print(obj.attr)  # Prints instance attribute, no __get__ call
Output
NonDataDescriptor __get__ called 42 100
🎯

When to Use Which

Choose a data descriptor when you need to control both reading and writing of an attribute, such as enforcing type checks, computed values, or side effects on assignment. This ensures your logic always runs and cannot be bypassed by instance variables.

Choose a non-data descriptor when you only need to control attribute access, like for methods or read-only computed properties, and you want to allow instance variables to override the descriptor if needed.

Key Takeaways

Data descriptors implement both __get__ and __set__, controlling attribute access and assignment.
Non-data descriptors implement only __get__, allowing instance variables to override them.
Data descriptors have higher priority in attribute lookup than instance variables.
Use data descriptors for managed attributes needing validation or side effects on assignment.
Use non-data descriptors for read-only or computed attributes where assignment override is allowed.