CodeWalk

Python描述符实现延迟加载属性(LazyLoading)

作者:专业代码师 · 2026-05-30 12:55

使用描述符实现一个延迟加载属性(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__,路径更短