Python property Function
Last modified April 11, 2025
This comprehensive guide explores Python's property function, which
creates managed attributes with getter, setter, and deleter methods. We'll cover
basic usage, decorator syntax, and practical examples.
Basic Definitions
The property function returns a property attribute. It provides a
way to encapsulate instance attribute access with methods. This enables data
validation, computed attributes, and read-only properties.
Key characteristics: takes optional getter, setter, deleter, and docstring parameters. Returns a property object that uses these methods when accessed. It's a built-in descriptor type in Python.
Basic Property Usage
Here's simple usage showing how to create a property with validation for a temperature class. The property ensures temperature stays above absolute zero.
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
def get_celsius(self):
return self._celsius
def set_celsius(self, value):
if value < -273.15:
raise ValueError("Temperature below absolute zero")
self._celsius = value
celsius = property(get_celsius, set_celsius)
temp = Temperature(25)
print(temp.celsius) # 25
temp.celsius = 30
print(temp.celsius) # 30
try:
temp.celsius = -300 # Raises ValueError
except ValueError as e:
print(e)
This example shows property creation with separate getter and setter methods. The property ensures temperature values are physically valid while maintaining a clean interface.
The _celsius naming convention indicates it's a protected attribute.
Users interact with the celsius property instead of the attribute.
Property Decorator Syntax
Python's decorator syntax provides a cleaner way to define properties. This example demonstrates the same temperature class using decorators.
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("Temperature below absolute zero")
self._celsius = value
temp = Temperature(25)
print(temp.celsius) # 25
temp.celsius = -100
print(temp.celsius) # -100
The decorator syntax is more readable and groups related methods together. The
@property decorator creates the getter, while @celsius.setter
creates the setter.
This approach is preferred in modern Python code as it clearly shows which methods belong to the property.
Read-Only Property
Properties can create read-only attributes by omitting the setter. This example shows a circle class with a computed read-only area property.
import math
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return math.pi * self.radius ** 2
circle = Circle(5)
print(circle.area) # 78.53981633974483
try:
circle.area = 100 # Raises AttributeError
except AttributeError as e:
print("Can't set area:", e)
The area property is computed from the radius and cannot be set
directly. Attempting to set it raises an AttributeError.
This pattern is useful for derived attributes that should only change when their source attributes change.
Property with Deleter
Properties can also define deleters for cleanup operations. This example shows a file handler class with property-based resource management.
class FileHandler:
def __init__(self, filename):
self._filename = filename
self._file = open(filename, 'r')
@property
def file(self):
return self._file
@file.deleter
def file(self):
if self._file:
self._file.close()
self._file = None
handler = FileHandler('example.txt')
print(handler.file.read(10))
del handler.file # Closes the file
The deleter ensures proper resource cleanup when the property is deleted. This
is more explicit than relying on __del__ or context managers.
The property provides controlled access to the file object while maintaining the ability to properly close it when needed.
Dynamic Property
Properties can compute values dynamically from other attributes. This example shows a rectangle class with dynamic width/height properties.
class Rectangle:
def __init__(self, x1, y1, x2, y2):
self.x1, self.y1 = x1, y1
self.x2, self.y2 = x2, y2
@property
def width(self):
return abs(self.x2 - self.x1)
@width.setter
def width(self, value):
if value <= 0:
raise ValueError("Width must be positive")
self.x2 = self.x1 + value
@property
def height(self):
return abs(self.y2 - self.y1)
@height.setter
def height(self, value):
if value <= 0:
raise ValueError("Height must be positive")
self.y2 = self.y1 + value
rect = Rectangle(0, 0, 10, 20)
print(rect.width, rect.height) # 10 20
rect.width = 15
print(rect.width) # 15
The width and height properties are computed from the coordinates but can also be set, which updates the coordinates. This maintains data consistency.
This approach is useful when you want to present different views of the same underlying data while keeping everything synchronized.
Best Practices
- Use for encapsulation: Hide implementation details behind properties
- Prefer decorator syntax: For cleaner, more readable code
- Document properties: Include docstrings explaining behavior
- Validate in setters: Ensure data remains consistent
- Consider performance: Computed properties may impact performance
Source References
Author
List all Python tutorials.