我们都知道,在Python的类中,dict保存了一个对象所有的属性,如下面的例子,我们建立了一个Circle的对象,对象的字典中保存着半径这个k-v值:
class Circle(object):
def __init__(self,radius):
self.radius = radius
c = Circle(2.5)
print(c.__dict__)
#{'radius': 2.5}
对于类中的方法,我们有时候希望它可以像属性一样被调用,这时候我们通常给类的方法添加@property修饰符:
class Circle(object):
def __init__(self,radius):
self.radius = radius
@property
def area(self):
print("calculate area")
return 3.14 * self.radius * 2
circle = Circle(4)
print(circle.area)
#calculate area
#25.12
但是这么做,虽然area可以当作一个属性访问,但是并不是真正的变成了一个属性,同时,我们每次调用circle.area,都会从头到尾执行一遍函数,我们来看下面的例子:
class Circle(object):
def __init__(self,radius):
self.radius = radius
@property
def area(self):
print("calculate area")
return 3.14 * self.radius * 2
c = Circle(4)
print(c.__dict__)
print(c.area)
print(c.area)
print(c.__dict__)
输出为:
{'radius': 4}
calculate area
25.12
calculate area
25.12
{'radius': 4}
可以发现,每次执行circle.area,整个函数流程都会被执行一次的,同时area也没有真正变成对象的属性,dict中并没有area。
那么我们有没有办法把一个类中的函数真正变成对象的属性,同时只有在第一次调用时进行一次计算,而之后每次调用不会重复计算呢?这就是Python中的lazy property。本文介绍两种方法。一种是使用python描述符,另一种是使用python修饰符。
1、使用python描述符
class lazy(object):
def __init__(self, func):
self.func = func
def __get__(self, instance, cls):
val = self.func(instance)
setattr(instance, self.func.__name__, val)
return val
class Circle(object):
def __init__(self, radius):
self.radius = radius
@lazy
def area(self):
print("calculate area")
return 3.14 * self.radius ** 2
c = Circle(4)
print('before calculate area')
print(c.__dict__)
print(c.area)
print(c.area)
print('after calculate area')
print(c.__dict__)
c.radius = 5
print(c.__dict__)
print(c.area)
输出为:
before calculate area
{'radius': 4}
calculate area
50.24
50.24
after calculate area
{'radius': 4, 'area': 50.24}
{'radius': 5, 'area': 50.24}
50.24
{'radius': 5, 'area': 50.24}
可以看到,area只在第一次调用时计算了一次,同时在调用以后area变成了对象的一个属性,同时值并不随半径的变化而变化。
2、使用python修饰符
def lazy_property(func):
attr_name = "_lazy_" + func.__name__
@property
def _lazy_property(self):
if not hasattr(self, attr_name):
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
c = Circle(4)
print('before calculate area')
print(c.__dict__)
print(c.area)
print(c.area)
print('after calculate area')
print(c.__dict__)
c.radius = 5
print(c.__dict__)
print(c.area)
输出为:
before calculate area
{'radius': 4}
50.24
50.24
after calculate area
{'radius': 4, '_lazy_area': 50.24}
{'radius': 5, '_lazy_area': 50.24}
50.24
可以看到,area只在第一次调用时计算了一次,同时在调用以后area变成了对象的一个属性,同时值并不随半径的变化而变化。