How Property Works Internally in Python: Explained Simply
In Python,
property works internally using the descriptor protocol, which means it defines methods like __get__, __set__, and __delete__ to control attribute access. When you use @property, Python creates a property object that manages getting, setting, and deleting values behind the scenes.Syntax
The property function creates a managed attribute by defining methods for getting, setting, and deleting its value.
Its basic syntax is:
property(fget=None, fset=None, fdel=None, doc=None)
Where:
fget: function to get the attribute valuefset: function to set the attribute valuefdel: function to delete the attribute valuedoc: optional documentation string
Using @property decorates a method as the getter, and you can define setter and deleter with @attribute.setter and @attribute.deleter.
python
class MyClass: def __init__(self): self._x = None def get_x(self): return self._x def set_x(self, value): self._x = value def del_x(self): del self._x x = property(get_x, set_x, del_x, "I'm the 'x' property.")
Example
This example shows how property controls access to a private attribute _temperature. The getter returns the value, the setter validates it, and the deleter removes it.
python
class Temperature: def __init__(self, temp=0): self._temperature = temp @property def temperature(self): return self._temperature @temperature.setter def temperature(self, value): if value < -273.15: raise ValueError("Temperature can't be below absolute zero!") self._temperature = value @temperature.deleter def temperature(self): print("Deleting temperature...") del self._temperature # Usage temp = Temperature(25) print(temp.temperature) # Calls getter try: temp.temperature = -300 # Calls setter, raises error except ValueError as e: print(e) temp.temperature = 100 # Valid set print(temp.temperature) del temp.temperature # Calls deleter
Output
25
Temperature can't be below absolute zero!
100
Deleting temperature...
Common Pitfalls
Common mistakes when using property include:
- Not using a private attribute (like
_x) inside getter/setter, causing infinite recursion. - Forgetting to define a setter when you want to assign values, leading to
AttributeError. - Misusing the decorator syntax, such as defining setter without the matching property name.
Here is an example of a common mistake and the correct way:
python
class Wrong: def __init__(self): self.x = 0 @property def x(self): return self.x # Wrong: calls itself recursively class Right: def __init__(self): self._x = 0 @property def x(self): return self._x # Correct: accesses private attribute @x.setter def x(self, value): self._x = value
Quick Reference
Summary tips for using property:
- Use a private attribute (like
_attr) to store data internally. - Define a getter method with
@propertyto access the value. - Define a setter method with
@property_name.setterto set the value safely. - Optionally, define a deleter with
@property_name.deleterto clean up. - Remember,
propertyuses the descriptor protocol internally to manage attribute access.
Key Takeaways
Python's property uses the descriptor protocol with __get__, __set__, and __delete__ methods internally.
Use @property to define a getter and @property_name.setter to define a setter for controlled attribute access.
Always store the actual data in a private attribute to avoid infinite recursion.
Defining a setter is necessary if you want to assign values to a property; otherwise, it is read-only.
Property helps encapsulate attribute access, allowing validation and control without changing the interface.