Real-world Applications
Property Management with Descriptors
class SecureAttribute:
def __init__(self, min_value=0, max_value=100):
self.min_value = min_value
self.max_value = max_value
self._values = {}
def __get__(self, instance, owner):
return self._values.get(instance, 0)
def __set__(self, instance, value):
if not (self.min_value <= value <= self.max_value):
raise ValueError(f"Value must be between {self.min_value} and {self.max_value}")
self._values[instance] = value
class Employee:
salary = SecureAttribute(min_value=1000, max_value=100000)
def __init__(self, name):
self.name = name
Lazy Loading Implementation
class LazyProperty:
def __init__(self, function):
self.function = function
self._cache = {}
def __get__(self, instance, owner):
if instance is None:
return self
if instance not in self._cache:
self._cache[instance] = self.function(instance)
return self._cache[instance]
class DataProcessor:
@LazyProperty
def complex_calculation(self):
## Simulate expensive computation
import time
time.sleep(2)
return sum(range(1000000))
Database ORM-like Descriptor
class DatabaseField:
def __init__(self, column_type):
self.column_type = column_type
self._values = {}
def __get__(self, instance, owner):
return self._values.get(instance)
def __set__(self, instance, value):
## Add type checking and validation
if not isinstance(value, self.column_type):
raise TypeError(f"Expected {self.column_type}, got {type(value)}")
self._values[instance] = value
class User:
name = DatabaseField(str)
age = DatabaseField(int)
Descriptor Use Cases
Application |
Description |
Key Benefits |
Data Validation |
Enforce input constraints |
Centralized validation |
Computed Properties |
Lazy evaluation |
Performance optimization |
Access Control |
Manage attribute access |
Enhanced security |
Caching |
Memoize expensive computations |
Improved efficiency |
Logging and Monitoring Descriptor
class LoggedAttribute:
def __init__(self):
self._values = {}
def __get__(self, instance, owner):
print(f"Accessing attribute for {instance}")
return self._values.get(instance)
def __set__(self, instance, value):
print(f"Setting attribute for {instance} to {value}")
self._values[instance] = value
class SystemMonitor:
cpu_usage = LoggedAttribute()
memory_usage = LoggedAttribute()
Descriptor Flow in Real Applications
graph TD
A[Attribute Access] --> B{Descriptor Present?}
B -->|Yes| C[Invoke Descriptor Methods]
C --> D{Validation Passed?}
D -->|Yes| E[Set/Get Value]
D -->|No| F[Raise Exception]
B -->|No| G[Normal Attribute Handling]
class cached_property:
def __init__(self, method):
self.method = method
self._cache = {}
def __get__(self, instance, owner):
if instance is None:
return self
if instance not in self._cache:
self._cache[instance] = self.method(instance)
return self._cache[instance]
Best Practices
- Use descriptors for cross-cutting concerns
- Keep descriptor logic simple
- Consider performance implications
- Validate input thoroughly
At LabEx, we recommend mastering descriptors to write more elegant and efficient Python code.