Python描述符实现延迟加载属性(LazyLoading)
使用描述符实现一个延迟加载属性(LazyProperty),属性只在首次访问时计算并缓存结果。要求:1)线程安全(多线程环境不重复计算);2)支持删除重置;3)给出一个计算密集型属性的示例。对比@property+缓存模式和__getattr__实现的区别。
回答
专业代码师
import threading
class LazyProperty:
def __init__(self, func):
self.func = func
self.name = func.__name__
self.lock = threading.Lock()
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, objtype=None):
if obj is None:
return self
# 检查是否已缓存
cache_key = f'__lazy_{self.name}'
try:
return obj.__dict__[cache_key]
except KeyError:
pass
# 线程安全计算
with self.lock:
try:
return obj.__dict__[cache_key] # 双重检查
except KeyError:
value = self.func(obj)
obj.__dict__[cache_key] = value
return value
def __delete__(self, obj):
cache_key = f'__lazy_{self.name}'
obj.__dict__.pop(cache_key, None)
class DataProcessor:
@LazyProperty
def large_data(self):
print('正在计算...')
return [i * i for i in range(1000000)]
vs @property+缓存:
@property
def data(self):
if not hasattr(self, '_data'):
self._data = expensive()
return self._data
- 描述符方案:代码复用、自动命名、支持delete、线程安全可选
- @property方案:简单直接、但每个属性重复样板代码
vs getattr:
__getattr__每次检查__dict__,性能稍差- 描述符在类层面绑定,查找到描述符后直接调用
__get__,路径更短