前言

今天学习一个开源项目的时候,发现其中一个很有意思的用法,叫做懒加载属性装饰器,他有什么用呢?

在你平时开发的时候,你可能会遇到这样的情况:某个对象的属性计算起来比较复杂,但又不能直接写死,避免后续不好修改,这个时候如果能做到“第一次用的时候才计算,后续访问直接使用缓存结果”,就能提高效率

这就是 懒加载属性(Lazy Property) 的作用。本文将带你一步步理解并实现一个 LazyProperty 装饰器。

什么是懒加载属性

懒加载属性是一种延迟计算 / 初始化的思想

好处:

  1. 节省不必要的计算开销

  2. 提高对象实例化速度

  3. 属性访问更高效(第一次之后几乎是 O(1))

为什么不用 @property

这里虽然将计算方法修饰为属性,但你每次调用的时候,实质上还是会重新计算

如果属性计算复杂或耗时,这样的重复计算就非常低效。

LazyProperty 的实现

class LazyProperty(object):
    """
    懒加载属性装饰器
    
    实现属性的延迟计算和结果缓存,避免重复计算。
    第一次访问属性时执行计算,后续访问直接返回缓存结果。
    """

    def __init__(self, func):
        """初始化懒加载装饰器
        
        Args:
            func: 被装饰的函数,用于计算属性值
        """
        self.func = func

    def __get__(self, instance, owner):
        """属性访问拦截器
        
        当通过实例访问被装饰的属性时,此方法会被调用。
        
        Args:
            instance: 访问属性的实例对象
            owner: 实例所属的类
            
        Returns:
            如果通过类访问,返回装饰器本身;
            如果通过实例访问,返回计算后的属性值(并缓存结果)。
        """
        if instance is None:
            # 通过类访问时(如 Config.serverHost),直接返回装饰器本身
            return self
        else:
            # 通过实例访问时,执行原始函数计算属性值
            value = self.func(instance)

            # 将计算结果缓存到实例上,覆盖掉原方法
            setattr(instance, self.func.__name__, value)

            # 返回计算结果
            return value

对于每一个获取函数而言,都加了一个懒加载的装饰器:

class Config:
    @LazyProperty
    def server_host(self):
        print("计算 serverPort...")
        return "127.0.0.1"

config = Config()

print(config.server_host)  # 第一次访问,会打印并返回结果
print(config.server_host)  # 第二次访问,直接返回缓存

第一次计算的时候,会将该函数传入到LazyProperty,然后进行计算,通过setattr将ConfigHandler的server_host的值改为计算结果,也就是说给这个对象的计算方法直接改为属性,所以下一次计算的时候就不会再调用函数,而且调用一个对象的属性。

更多推荐