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
  • 相关阅读:
    pyqt信号和槽传递额外参数
    PyQt--QTreeWidget
    转载:futex同步机制详解
    Linux 下的同步机制
    Linux 下线程的理解
    Linux下的物理内存管理2-slab缓存的管理
    转:C语言的编译链接过程的介绍
    LInux中ThreadInfo中的preempt_count字段
    LInux中的物理内存管理
    Linux下的内核抢占
  • 原文地址:https://www.cnblogs.com/c-x-a/p/9170433.html
Copyright © 2011-2022 走看看