看本文之前,可以先阅读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值
重点:
- 跟[
print(t.des)
](#3.object.__get__(self, instance, owner)
)会触发t.des指向的descriptor实例的__get__
一样,通过类__dict__["serverHost"]
,其也是个描述器实例,因此也会触发LazyProperty object的__get__
- 实例
__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
网友评论