python学习笔记6.6-类的惰性属性

我们想将一个只读属性定义为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函数来处理。这比直接在实例字典中查找相应的值要慢一些。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏从零开始的linux

mongodb分片

分别在三台机器上面创建 mkdir -pv /data/mongodb/mongos/log mkdir -pv /data/mongodb/config/{d...

3414
来自专栏吴伟祥

Apache POI总结 原

Apache POI  是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格...

651
来自专栏牛肉圆粉不加葱

ResourceManager HA无法连接Spark TrackUi现象解决方案

在对ResourceManager做了基于Zookeeper的HA后, 在YARN集群上执行Spark application后, 打开Spark Applic...

682
来自专栏用户画像

省份和城市的级联下拉菜单

692
来自专栏余生开发

echarts太阳分布图-饼图来回穿梭

var dom = document.getElementById("container");

542
来自专栏Android干货

Android项目实战(八):列表右侧边栏拼音展示效果

2745
来自专栏linux驱动个人学习

ALSA声卡驱动的DAPM(一)-DPAM详解

最近使用tinymix 调试相应的音频通道,但是一直不知道音频通道的原理是什么。所以百度了一下,百度结果是与DPAM有关。 一、DAPM简介:  DAPM是Dy...

3596
来自专栏张善友的专栏

Div Scroll Bar (用层模拟滚动条)

对Div的盒模型以及Css控制定位都不熟,所以遇到了不少BT问题……好在最终突破了种种困难,基本实现了自己想要的效果。 说明: 最大的突破是通过了 xhtml...

18510
来自专栏Data Analysis & Viz

手把手教你完成一个数据科学小项目(6):城市提取与可视化

请先阅读《“中国年轻人正带领国家走向危机”,这锅背是不背? 》 一文,以对“手把手教你完成一个数据科学小项目”系列有个全局性的了解。

782
来自专栏文武兼修ing——机器学习与IC设计

关于空难数据集的探索分析导入数据集伤亡分析机型处理时间分析

写在前面: 这是我见过的最严肃的数据集,几乎每一行数据背后都是生命和鲜血的代价。这次探索分析并不妄图说明什么,仅仅是对数据处理能力的锻炼。因此本次的探索分析...

3625

扫码关注云+社区