美文网首页自动化技术文
让 Python 中类的属性具有惰性求值的能力

让 Python 中类的属性具有惰性求值的能力

作者: 51reboot | 来源:发表于2018-08-21 08:54 被阅读0次

起步

我们希望将一个只读的属性定义为 property 属性方法,只有在访问它时才进行计算。但是,又希望把计算出的值缓存起来,不要每次访问它时都重新计算。

解决方案

定义一个惰性属性最有效的方法就是利用描述符类来完成它,示例如下:

class lazyproperty:
   def __init__(self, fun):
       self.fun = fun

   def __get__(self, instance, owner):
       if instance is None:
           return self
       value = self.fun(instance)
       setattr(instance, self.fun.__name__, value)
       return value

要使用这个工具,可以像下面的方式来使用它:

class Circle:
   def __init__(self, radius):
       self.radius = radius


   @lazyproperty
   def area(self):
       print('Computing area')
       return 3.1415 * self.radius ** 2

c = Circle(5)
print(c.area)
print(c.area)

可以看出,这里的实例方法 area() 只会被调用一次。

为什么会这样

如果类中定义了 get()、set() 、delete() 中的任何方法,那么这个就被成为描述符(descriptor)。一般情况下(我是说一般情况下),访问属性的默认行为是从对象的字典中获取,并沿着一个查找链的顺序进行搜索,比如对于 a.x 有一个查找链,从 a.dict['x'] 然后是 type(a).dict['x'],再继续通过 type(a) 的基类开始。

而如果查找的值是一个描述符对象,则会覆盖这个默认的搜索行为,优先采用描述符的行为,这个行为会因为如果调用而有些不同。这里就只说明例子中的情况。

如果描述符绑定的对象实例,a.x 则转换为调用: type(a).dict['x'].get(a, type(a))。当一个描述符之定义 get() 方法,则它的绑定关系比一般情况下要弱化很多。特别是,只有当被访问的属性不存在对象字典中时,get() 才会被调用。

更多描述可见文档:https://docs.python.org/3/reference/datamodel.html?#object.

这种惰性求值的方法在很多模块中都会使用,比如 django 中的 cached_property:

用上与例子一致,如表单中的 changed_data :

讨论

在大部分情况下,让属性具有惰性求值能力的全部意义就在于提升程序性能。当不需要这个属性时就能避免进行无意义的计算,同时又能阻止该属性重复进行计算。

本文的技巧中有一个潜在的缺点,就是计算出的值后就变成可变的(mutable)。

>>> c.area
78.53
>>> c.area = 3
>>> c.area
3

如果考虑可变性的问题,可以使用另一种实现方式,但执行效率会稍打折扣:

def lazyproperty(func):
   name = '_lazy_' + func.__name__
   @property
   def lazy(self):
       if hasattr(self, name):
           return getattr(self, name)
       value = func(self)
       setattr(self, name, value)
       return value

   return lazy

如果使用这种方式,就会发现 set 操作是不允许的,所有的 get 操作都必须经由属性的 getter 函数来处理,这比直接在实例字典中查找相应的值要慢一些。

参考

作者:weapon
转载|原文链接:https://segmentfault.com/a/1190000016015066
(如有侵权,请联系删除)

最新公告通知

第 19 期【Python自动化运维入门】正在火热招生中
第 8 期 【Python自动化运维进阶】正在火热招生中

相关文章

  • 让 Python 中类的属性具有惰性求值的能力

    起步 我们希望将一个只读的属性定义为 property 属性方法,只有在访问它时才进行计算。但是,又希望把计算出的...

  • Swift Collection 中的 lazy 作用

    惰性求值 惰性求值常见于函数式编程中,也有人把惰性求值翻译成延迟求值(Lazy Evaluation)。它的目的是...

  • python 类

    类 用来描述具有相同的属性和方法的对象的集合。python中的类就是对象。 python 中的元类详解 创建Dog...

  • Python中的优化:惰性求值详解

    惰性求值,也就是延迟求值,表达式不会在它被绑定到变量之后就立即求值,而是等用到时再求值。这个特性可以解决一些巨大甚...

  • Swift 之惰性求值

    Swift 之惰性求值 在 Haskell 中的函数有一个特性 从而引出一个概念 惰性求值 下面有段 分析 惰性求...

  • 23.Python之面向对象(类与对象)

    Python之类与对象 类类:用来描述具有相同的属性和方法的对象的集合(在Python中类与类型是一个概念)。特点...

  • day014 笔记 08-01

    内置类的属性 python中每个类都拥有内置的类属性。 私有化 python中类中的属性和方法的私有化:直接在属性...

  • Python创建类、构造函数和析构函数、创建实例对象(学习笔记)

    一、Python类定义 类是用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。...

  • 4.2 Variations on a Scheme: Lazy

    在元循环求值器的基础上,我们能够实现变体形式 惰性求值(lazy evaluation) 器。惰性求值器能够将程式...

  • day13面向对象(1)

    01-内置类属性 内置类属性:python中每个类都拥有内置的类属性 02私有化 python中,类和属性的私有化...

网友评论

    本文标题:让 Python 中类的属性具有惰性求值的能力

    本文链接:https://www.haomeiwen.com/subject/naqdiftx.html