Automatically setting getter, setter and deleter in python
Categories:
Simplifying Python Properties: Automatic Getters, Setters, and Deleters

Learn how to effortlessly manage object attributes in Python using decorators to automatically define getters, setters, and deleters, enhancing code readability and maintainability.
In Python, properties provide a way to customize access to instance attributes. They allow you to define methods that are called when an attribute is accessed (getter), assigned (setter), or deleted (deleter). While powerful, manually defining these for multiple attributes can lead to repetitive code. This article explores how to automate the creation of getters, setters, and deleters using decorators, making your Python classes cleaner and more Pythonic.
Understanding Python Properties
Before diving into automation, let's briefly review how Python's built-in property()
function and the @property
decorator work. A property allows you to encapsulate attribute access, enabling you to add logic (like validation or side effects) without changing the way the attribute is accessed from outside the class. This is crucial for maintaining the principle of encapsulation and providing a clean interface for your objects.
class MyClass:
def __init__(self, value):
self._value = value
@property
def value(self):
"""The 'value' property."""
print("Getting value")
return self._value
@value.setter
def value(self, new_value):
print(f"Setting value to {new_value}")
if not isinstance(new_value, (int, float)):
raise ValueError("Value must be a number")
self._value = new_value
@value.deleter
def value(self):
print("Deleting value")
del self._value
# Usage
obj = MyClass(10)
print(obj.value) # Calls getter
obj.value = 20 # Calls setter
del obj.value # Calls deleter
# print(obj.value) # Would raise AttributeError
Basic usage of Python's @property
decorator for getter, setter, and deleter.
The Challenge of Repetition
While the @property
decorator is elegant, imagine a class with many attributes, each requiring similar getter, setter, and deleter logic. You would end up writing @property
, @<attribute>.setter
, and @<attribute>.deleter
blocks repeatedly. This boilerplate code can obscure the core logic of your class and make it harder to maintain. Our goal is to reduce this repetition.
flowchart TD A[Start] B{Multiple Attributes?} C[Manual Property Definition] D[Repetitive Code] E[Automated Property Definition] F[Clean, Maintainable Code] A --> B B -- Yes --> C C --> D B -- No --> F D --> E E --> F
Flowchart illustrating the problem of repetitive property definitions and the solution of automation.
Automating Properties with a Decorator
We can create a custom class decorator that automatically generates properties for specified attributes. This decorator will iterate through a list of attribute names and dynamically create the getter, setter, and deleter methods, assuming a common pattern (e.g., storing the actual value in a private attribute like _attribute_name
).
def auto_property(*attrs):
def decorator(cls):
for attr_name in attrs:
private_attr = f'_{attr_name}'
# Create getter
def getter(self, name=attr_name):
print(f"Getting {name}")
return getattr(self, f'_{name}')
# Create setter
def setter(self, value, name=attr_name):
print(f"Setting {name} to {value}")
setattr(self, f'_{name}', value)
# Create deleter
def deleter(self, name=attr_name):
print(f"Deleting {name}")
delattr(self, f'_{name}')
# Bind the methods to the class as a property
setattr(cls, attr_name, property(getter, setter, deleter))
return cls
return decorator
@auto_property('name', 'age')
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
# Usage
p = Person("Alice", 30)
print(p.name) # Calls auto-generated getter
p.age = 31 # Calls auto-generated setter
del p.name # Calls auto-generated deleter
# print(p.name) # Would raise AttributeError
A custom auto_property
decorator to automatically create getters, setters, and deleters.
auto_property
decorator uses setattr
to dynamically add the property to the class. Note the use of default arguments in the nested getter
, setter
, and deleter
functions to capture the attr_name
from the loop's scope, preventing late binding issues.Enhancing the Decorator with Custom Logic
The basic auto_property
decorator is useful for simple cases, but often you need specific validation or side effects for certain attributes. We can extend the decorator to allow for custom getter, setter, or deleter functions to be provided, falling back to the default behavior if not specified. This provides flexibility while still reducing boilerplate.
def flexible_auto_property(*attrs):
def decorator(cls):
for attr_name in attrs:
private_attr = f'_{attr_name}'
# Check if custom methods exist in the class
custom_getter = getattr(cls, f'get_{attr_name}', None)
custom_setter = getattr(cls, f'set_{attr_name}', None)
custom_deleter = getattr(cls, f'del_{attr_name}', None)
# Define default getter
def default_getter(self, name=attr_name):
print(f"Default getting {name}")
return getattr(self, f'_{name}')
# Define default setter
def default_setter(self, value, name=attr_name):
print(f"Default setting {name} to {value}")
setattr(self, f'_{name}', value)
# Define default deleter
def default_deleter(self, name=attr_name):
print(f"Default deleting {name}")
delattr(self, f'_{name}')
# Use custom method if available, otherwise use default
getter_func = custom_getter if custom_getter else default_getter
setter_func = custom_setter if custom_setter else default_setter
deleter_func = custom_deleter if custom_deleter else default_deleter
setattr(cls, attr_name, property(getter_func, setter_func, deleter_func))
return cls
return decorator
@flexible_auto_property('name', 'score')
class GamePlayer:
def __init__(self, name, score):
self._name = name
self._score = score
# Custom setter for score with validation
def set_score(self, value):
print(f"Custom setting score to {value}")
if not isinstance(value, int) or value < 0:
raise ValueError("Score must be a non-negative integer")
self._score = value
# Usage
p = GamePlayer("PlayerOne", 100)
print(p.name) # Uses default getter
p.name = "P2" # Uses default setter
p.score = 150 # Uses custom setter
# p.score = -1 # Would raise ValueError
del p.name # Uses default deleter
A more flexible flexible_auto_property
decorator allowing custom logic for specific attributes.
get_attributeName
, set_attributeName
, and del_attributeName
within the class. If found, it uses them; otherwise, it falls back to the generic getter/setter/deleter. This provides a powerful balance between automation and customization.