Python basic tutorial: lazy attributes (lazy initialization)

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_propertyand the method is _lazy_propertycalled 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)

Guess you like

Origin blog.csdn.net/qdPython/article/details/112611339