zoukankan      html  css  js  c++  java
  • 类的惰性属性

    我们想将一个只读属性定义为property属性方法,只有在访问它时才参与计算。同时,一旦访问了该属性,希望把计算出来的值缓存起来,不要每次访问它时都要重新计算。这样就能很大程度上提升程序的性能。定义一个惰性属性最简单的方法就是利用描述符来完成。

    #define a lazyproperty
    class Lazyproperty:
        def __init__(self,func):
            self.func = func
        def __get__(self,instance,cls):
            if instance is None:
                return self
            else:
                value = self.func(instance)
                setattr(instance,self.func.__name__,value)
                return value
    
    #Example
    import math
    class Circle:
        def __init__(self,radius):
            self.radius = radius
    
        @Lazyproperty
        def area(self):
            print('Computing area')
            return math.pi*self.radius*2
        @Lazyproperty
        def perimeter(self):
            print('Computing perimeter')
            return 2*math.pi*self.radius
    
    c = Circle(4.0)
    print(c.radius)
    print('calling c.area the first time:')
    print(c.area)
    print('calling c.area the second time:')
    print(c.area)
    
    打印输出:
    4.0
    calling c.area the first time:
    Computing area
    25.132741228718345
    calling c.area the second time:
    25.132741228718345

    从示例中可以很清楚的看出,第一次调用c.area时计算过程被执行,第二次调用它的时候,计算过程没有执行,是因为计算一次之后,它的值就被储存起来了,第二次直接拿出来用,从而加快了程序的运行。

    前面提到描述符的时候讲过,当吧描述符放到类的定义体中的时候,访问它的属性会出发get(),set(),delete()方法。但是,如果一个描述符只定义了get()方法,则它的绑定关系比一般情况要弱化的多。特别是,只有当被访问的属性不在底层的实例字典中时,_get_()方法会得到调用。

    但是,这种技术有一个潜在是bug,一旦使用了这种方法,计算的值就会变成可变的了。

    c.area = 66
    print(c.area)
    打印输出:66

    如果考虑到可变性,可以使用一种方法去修复这个bug,但是同时执行效率也会大大的降低。

    def lazyproperty(func):
        name = '_lazy_' + func.__name__
        @property
        def lazy(self):
            if hasattr(self,name):
                return getattr(self,name)
            else:
                value = func(self)
                setattr(self,name,value)
                return value
        return lazy
    
    #Example
    import math
    class Circle:
        def __init__(self,radius):
            self.radius = radius
    
        @lazyproperty
        def area(self):
            print('Computing area')
            return math.pi*self.radius*2
        @lazyproperty
        def perimeter(self):
            print('Computing perimeter')
            return 2*math.pi*self.radius
    
    c = Circle(4.0)
    print(c.radius)
    print('calling c.area the first time:')
    print(c.area)
    print('calling c.area the second time:')
    print(c.area)
    
    打印输出:
    4.0
    calling c.area the first time:
    Computing area
    25.132741228718345
    calling c.area the second time:
    25.132741228718345

    从该实例中可以发现,达到了同样的效果。

    c.area = 66
    print(c.area)
    
    Traceback (most recent call last):
      File "D:/home/WX/test_lazyproperty.py", line 32, in <module>
        c.area = 66
    AttributeError: can't set attribute

    之后改变c.area的值,发现报错,不能被修改。这样就修复了第一种方法中计算值可以被外部改变的bug。这种方法的缺点就是所有的get操作都必须经由属性的getter函数来处理。这比直接在实例字典中查找相应的值要慢一些。

    版权声明:本文为博主原创文章,未经博主允许不得转载。本博客所用Python软件环境:win10+anaconda3+pycharm,Python版本:3.6 https://blog.csdn.net/jinxiaonian11/article/details/53729836
  • 相关阅读:
    129. Sum Root to Leaf Numbers
    113. Path Sum II
    114. Flatten Binary Tree to Linked List
    112. Path Sum
    100. Same Tree
    300. Longest Increasing Subsequence
    72. Edit Distance
    自定义js标签库
    JS 实现Table相同行的单元格自动合并示例代码
    mysql 高版本only_full_group_by 错误
  • 原文地址:https://www.cnblogs.com/c-x-a/p/9170433.html
Copyright © 2011-2022 走看看