Python中的属性拦截与属性管理(`__getattr__`、`__getattribute__`、`__setattr__`、`__delattr__`)实战案例
字数 595 2025-11-09 08:11:27
Python中的属性拦截与属性管理(__getattr__、__getattribute__、__setattr__、__delattr__)实战案例
我将通过一个实际的缓存属性案例来展示这些方法的综合应用。假设我们要创建一个类,其中的某些属性需要经过复杂计算,但计算结果可以被缓存以提高性能。
案例需求:创建一个ExpensiveObject类,其中的expensive_value属性需要复杂计算,但计算后结果应该被缓存。同时,如果对象的其他属性被修改,缓存应该自动失效。
实现步骤:
- 基础类结构:
class ExpensiveObject:
def __init__(self, base_value):
self.base_value = base_value
self._cache = {} # 用于存储缓存结果
self._dirty = True # 标记缓存是否过期
- 实现
__setattr__控制属性设置:
def __setattr__(self, name, value):
# 先调用父类的设置方法
super().__setattr__(name, value)
# 如果设置了base_value,标记缓存为过期
if name == 'base_value':
super().__setattr__('_dirty', True)
- 实现
__getattribute__拦截所有属性访问:
def __getattribute__(self, name):
# 对于expensive_value属性,我们特殊处理
if name == 'expensive_value':
# 先获取_dirty标志(注意避免递归)
dirty = super().__getattribute__('_dirty')
cache = super().__getattribute__('_cache')
if dirty or 'expensive_value' not in cache:
print("重新计算expensive_value...")
# 模拟复杂计算
base = super().__getattribute__('base_value')
result = base * 2 + 10 # 模拟复杂计算
cache['expensive_value'] = result
super().__setattr__('_dirty', False)
return cache['expensive_value']
# 其他属性正常访问
return super().__getattribute__(name)
- 实现
__getattr__处理不存在的属性:
def __getattr__(self, name):
# 当访问不存在的属性时,提供友好的错误信息
if name.startswith('calc_'):
raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{name}'. "
f"Did you mean 'expensive_value'?"
)
raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{name}'"
)
- 完整代码与测试:
# 完整类定义
class ExpensiveObject:
def __init__(self, base_value):
self.base_value = base_value
self._cache = {}
self._dirty = True
def __setattr__(self, name, value):
super().__setattr__(name, value)
if name == 'base_value':
super().__setattr__('_dirty', True)
def __getattribute__(self, name):
if name == 'expensive_value':
dirty = super().__getattribute__('_dirty')
cache = super().__getattribute__('_cache')
if dirty or 'expensive_value' not in cache:
print("重新计算expensive_value...")
base = super().__getattribute__('base_value')
result = base * 2 + 10
cache['expensive_value'] = result
super().__setattr__('_dirty', False)
return cache['expensive_value']
return super().__getattribute__(name)
def __getattr__(self, name):
if name.startswith('calc_'):
raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{name}'. "
f"Did you mean 'expensive_value'?"
)
raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{name}'"
)
# 测试代码
def test_expensive_object():
obj = ExpensiveObject(5)
print("第一次访问expensive_value:")
print(obj.expensive_value) # 输出: 重新计算expensive_value... 20
print("\n第二次访问(应该从缓存读取):")
print(obj.expensive_value) # 输出: 20(没有重新计算的消息)
print("\n修改base_value使缓存失效:")
obj.base_value = 10
print(obj.expensive_value) # 输出: 重新计算expensive_value... 30
print("\n测试不存在的属性:")
try:
print(obj.nonexistent)
except AttributeError as e:
print(f"错误: {e}")
try:
print(obj.calc_value)
except AttributeError as e:
print(f"友好错误: {e}")
if __name__ == "__main__":
test_expensive_object()
关键要点总结:
__getattribute__:拦截所有属性访问,包括存在的和不存在的属性__getattr__:只在属性不存在时被调用,用于友好的错误处理__setattr__:拦截所有属性设置操作,用于维护缓存一致性- 避免递归:在这些方法内访问属性时,必须使用
super()来避免递归调用 - 缓存策略:通过
_dirty标志智能管理缓存,确保数据一致性
这个实战案例展示了如何综合运用属性管理方法来实现智能缓存功能,这是实际开发中常见的应用场景。