Lazy initialization of a Python object means that it is initialized when it is created for the first time, or the result of the first creation is saved, and then the result is returned directly every time it is called. Delayed initialization is mainly used to improve performance, avoid wasting calculations, and reduce the memory requirements of the program.
1. Review property
property can turn attribute access into method call
class Circle(object):
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14 * self.radius ** 2
c = Circle(4)
print c.radius
print c.area
As you can see, although area is defined as a method, after adding @property, c.area can be executed directly as a property access.
Now the problem is, every time c.area is called, it will be calculated once, which is too wasteful of cpu. How can I calculate it only once? This is lazy property
2.lazy property implementation
There are two ways to implement lazy initialization, one is to use python descriptors, and the other is to use the @property modifier
method 1:
'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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 'evalute'
return 3.14 * self.radius ** 2
c = Circle(4)
print c.radius
print c.area
print c.area
print c.area
As a result,'evalute' was output only once. In the lazy class, we define the __get__()
method, so it is a descriptor. When we first performance c.area, python interpreter will start c.__dict__
in progress to find, not found, the Circle.__dict__
lookup in this case because the area is defined as a descriptor, so call the __get__
method.
In the __get__()
method, call the area() method of the instance to calculate the result, and dynamically add an attribute area with the same name to the instance, and then assign the calculated value to it, which is equivalent to setting c.__dict__['area']=val
.
When we call c.area again, directly from the c.__dict__
lookup in, then the calculated value before it will return directly.
Method 2:
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):
print 'evalute'
return 3.14 * self.radius ** 2
This is the same as method 1, adding @lazy_property before area() is equivalent to running the following code:
lazy_property(area)
lazy_property()
The method returns _lazy_property
and the method is _lazy_property
called again _lazy_property()
, and the remaining operations are similar to method 1.
'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
#性能差方法
class Circle(object):
def __init__(self, radius):
self.radius = radius
@property
def area(self):
print("come in")
return 3.14 * self.radius ** 2
c = Circle(4)
print(c.radius)
print(c.area)
print(c.area)
#方法1
class LazyProperty:
def __init__(self, method):
self.method = method
def __get__(self, instance, cls):
if not instance:
return None
value = self.method(instance)
setattr(instance,self.method.__name__,value)
return value
class Circle(object):
def __init__(self, radius):
self.radius = radius
@LazyProperty
def area(self):
print("come in")
return 3.14 * self.radius ** 2
c = Circle(4)
print(c.radius)
print(c.area)
print(c.area)
#方法2
def LazyProperty(func):
attr_name = "_lazy_" + func.__name__
@property
def wrap(self):
if not hasattr(self, attr_name):
setattr(self, attr_name, func(self))
return getattr(self, attr_name)
return wrap
class Circle(object):
def __init__(self, radius):
self.radius = radius
@LazyProperty
def area(self):
print("come in")
return 3.14 * self.radius ** 2
c = Circle(4)
print(c.radius)
print(c.area)
print(c.area)