python 将计算结果保留到缓存中

Python011

python 将计算结果保留到缓存中,第1张

定义一个延迟属性的一种高效方法是通过使用一个描述器类,如下所示:

<pre style="box-sizing: border-boxfont-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospacefont-size: 12pxwhite-space: premargin: 0pxpadding: 12pxdisplay: blockoverflow: autoline-height: 1.4">class lazyproperty:

def init (self, func):

self.func = func

</pre>

你需要像下面这样在一个类中使用它:

<pre style="box-sizing: border-boxfont-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospacefont-size: 12pxwhite-space: premargin: 0pxpadding: 12pxdisplay: blockoverflow: autoline-height: 1.4">import math

class Circle:

def init (self, radius):

self.radius = radius

</pre>

下面在一个交互环境中演示它的使用:

<pre style="box-sizing: border-boxfont-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospacefont-size: 12pxwhite-space: premargin: 0pxpadding: 12pxdisplay: blockoverflow: autoline-height: 1.4">>>>c = Circle(4.0)

</pre>

仔细观察你会发现消息 Computing area 和 Computing perimeter 仅仅出现一次。

很多时候,构造一个延迟计算属性的主要目的是为了提升性能。 例如,你可以避免计算这些属性值,除非你真的需要它们。 这里演示的方案就是用来实现这样的效果的, 只不过它是通过以非常高效的方式使用描述器的一个精妙特性来达到这种效果的。

正如在其他小节(如8.9小节)所讲的那样,当一个描述器被放入一个类的定义时, 每次访问属性时它的 __get__() 、 __set__() 和 __delete__() 方法就会被触发。 不过,如果一个描述器仅仅只定义了一个 __get__() 方法的话,它比通常的具有更弱的绑定。 特别地,只有当被访问属性不在实例底层的字典中时 __get__() 方法才会被触发。

lazyproperty 类利用这一点,使用 __get__() 方法在实例中存储计算出来的值, 这个实例使用相同的名字作为它的property。 这样一来,结果值被存储在实例字典中并且以后就不需要再去计算这个property了。 你可以尝试更深入的例子来观察结果:

<pre style="box-sizing: border-boxfont-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospacefont-size: 12pxwhite-space: premargin: 0pxpadding: 12pxdisplay: blockoverflow: autoline-height: 1.4">>>>c = Circle(4.0)

</pre>

这种方案有一个小缺陷就是计算出的值被创建后是可以被修改的。例如:

<pre style="box-sizing: border-boxfont-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospacefont-size: 12pxwhite-space: premargin: 0pxpadding: 12pxdisplay: blockoverflow: autoline-height: 1.4">>>>c.area

Computing area

50.26548245743669

</pre>

如果你担心这个问题,那么可以使用一种稍微没那么高效的实现,就像下面这样:

<pre style="box-sizing: border-boxfont-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospacefont-size: 12pxwhite-space: premargin: 0pxpadding: 12pxdisplay: blockoverflow: autoline-height: 1.4">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

</pre>

如果你使用这个版本,就会发现现在修改操作已经不被允许了:

<pre style="box-sizing: border-boxfont-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospacefont-size: 12pxwhite-space: premargin: 0pxpadding: 12pxdisplay: blockoverflow: autoline-height: 1.4">>>>c = Circle(4.0)

结果为7.5。

在Python" // "表示整数除法。

Python的表达式写法与C/C++类似。只是在某些写法有所差别。主要的算术运算符与C/C++类似。+, -, *, /, //, **, ~, %分别表示加法或者取正、减法或者取负、乘法、除法、整除、乘方、取补、取余。>>, <<表示右移和左移。&, |, ^表示二进制的AND, OR, XOR运算。

Python

是完全面向对象的语言。函数、模块、数字、字符串都是对象。并且完全支持继承、重载、派生、多继承,有益于增强源代码的复用性。Python支持重载运算符和动态类型。相对于Lisp这种传统的函数式编程语言,Python对函数式设计只提供了有限的支持。有两个标准库(functools, itertools)提供了Haskell和Standard ML中久经考验的函数式程序设计工具。