Python中实现lazy_property懒加载属性

2022-04-02  本文已影响0人  我只要喝点果粒橙

看本文之前,可以先阅读Python中的lazy property,概念与用法在上述文章中有比较详细的介绍,本文是对其未讲解清晰地方的具体分析。

描述符+类装饰器实现


class LazyProperty(object):
    def __init__(self, func):
        # 初始化func为serverHost函数
        self.func = func

    def __get__(self, instance, owner):
        if instance is None:
            return self
        else:
            value = self.func(instance)
            # 会将结果值通过setattr方法存入到instance实例的__dict__中
            setattr(instance, self.func.__name__, value)
            return value


class ConfigHandler:
    def __init__(self):
        pass
    # 返回一个LazyProperty实例 
    @LazyProperty
    def serverHost(self):
        return os.environ.get("HOST", setting.HOST)

setting = namedtuple("setting", ["HOST"])
setting.HOST = "g"

# 测试一
a = ConfigHandler()
print(a.__dict__)
# 1. 注解先是创建了LazyProperty(serverHost)的实例
# 2. 再是语法糖进行了赋值serverHost = LazyProperty(serverHost)
# 3. 当第一次进行调用的时候, instance = configHandler**实例**, self.func(instance实例) == 调用serverHost(instance实例)从而获得了真正值value。而之后的 setattr处将self实例的__dict__中添加了serverHost-value,再次访问self.serverHost时, 已经不再是函数, 而是value值(serverHost不再从ConfigHandler.__dict__中取, 而是实例a.__dict__中取)
print(a.serverHost)
print("say")
print(a.__dict__)

# 测试二
# 如果先执行类的__dict__能看到类的serverHost是一个**描述器对象实例**=> 'serverHost': <__main__.LazyProperty object at 0x0000020A1AEB7FD0>
print(ConfigHandler.__dict__)
# 通过__dir__能见到serverHost为实例的一个方法
print(a.__dir__())
# 此时a.__dict__为空
print(a.serverHost)
# 等到调用过a.serverHost后可以发现a.__dict__中多了serverHost
print(a.__dict__)
# 由于实例__dict__会优先于类的__dict__使用,所以直接返回了value值

重点:

  1. 跟[print(t.des)](#3.object.__get__(self, instance, owner))会触发t.des指向的descriptor实例的__get__一样,通过类__dict__["serverHost"],其也是个描述器实例,因此也会触发LazyProperty object的__get__
  2. 实例__dict__会优先于类的__dict__使用,如果实例__dict__找不到,会往上类__dict__

修饰符(方法装饰器)

def lazy_property(func):
    # 创建protected属性
    attr_name = "_lazy_" + func.__name__
    @property
    def _lazy_property(self):
        # print("done")
        if not hasattr(self, attr_name):
            # print("set")
            setattr(self, attr_name, func(self))
        return getattr(self, attr_name)
    return _lazy_property


class Circle(object):
    def __init__(self, radius):
        self.radius = radius

    @lazy_property
    def area(self):
        return 3.14 * self.radius ** 2

# 当解析Circle类、定义area方法的时候,会将Circle.area = @property修饰的_lazy_property函数
c = Circle(4)
print('before calculate area')
print(c.__dict__)
# 当调用c.area时,会输出done, 此时会执行_lazy_property内的具体函数, 此次会进行setattr
print(c.area)
# 此次不会调用setattr
print(c.area)
print('after calculate area')
print(c.__dict__)
c.radius = 5
上一篇 下一篇

猜你喜欢

热点阅读