Object Customization
Advanced Object Customization Techniques
Object customization in Python allows developers to create flexible, powerful, and intelligent objects that can adapt to various programming scenarios.
Descriptor Protocol
Descriptors provide a way to customize attribute access:
class ValidatedAttribute:
def __init__(self, min_value=None, max_value=None):
self.min_value = min_value
self.max_value = max_value
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
return instance.__dict__.get(self.name)
def __set__(self, instance, value):
if self.min_value is not None and value < self.min_value:
raise ValueError(f"Value must be at least {self.min_value}")
if self.max_value is not None and value > self.max_value:
raise ValueError(f"Value must be at most {self.max_value}")
instance.__dict__[self.name] = value
class Person:
age = ValidatedAttribute(0, 120)
def __init__(self, name, age):
self.name = name
self.age = age
Metaclasses allow deep customization of class creation:
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class DatabaseConnection(metaclass=SingletonMeta):
def __init__(self, connection_string):
self.connection_string = connection_string
Customization Techniques Comparison
| Technique |
Use Case |
Complexity |
Flexibility |
| Magic Methods |
Basic behavior modification |
Low |
Medium |
| Descriptors |
Attribute access control |
Medium |
High |
| Metaclasses |
Class creation customization |
High |
Very High |
Proxy Objects
class LazyProperty:
def __init__(self, function):
self.function = function
self._value = None
def __get__(self, instance, owner):
if self._value is None:
self._value = self.function(instance)
return self._value
class ExpensiveResource:
@LazyProperty
def complex_calculation(self):
## Simulate expensive computation
import time
time.sleep(2)
return sum(range(1000000))
Dependency Injection Pattern
class ServiceContainer:
def __init__(self):
self._services = {}
def register(self, service_type, service_implementation):
self._services[service_type] = service_implementation
def resolve(self, service_type):
return self._services.get(service_type)
## Visualization of Dependency Injection
```mermaid
graph TD
A[Service Container] --> B[Service Registration]
A --> C[Service Resolution]
B --> D[Service Type]
B --> E[Service Implementation]
C --> F[Retrieve Specific Service]
Advanced Customization Strategies
- Use composition over inheritance
- Implement clean, focused interfaces
- Leverage Python's dynamic nature
- Follow SOLID principles
Error Handling and Validation
class StrictDict(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._validators = {}
def add_validator(self, key, validator):
self._validators[key] = validator
def __setitem__(self, key, value):
validator = self._validators.get(key)
if validator and not validator(value):
raise ValueError(f"Invalid value for {key}")
super().__setitem__(key, value)
Best Practices
- Keep customizations simple and predictable
- Document custom behaviors clearly
- Test extensively
- Consider performance implications
By mastering these object customization techniques, you'll write more flexible and powerful Python code. LabEx encourages continuous learning and experimentation with these advanced concepts.