将memoization装饰器理解为闭包

时间:2016-08-27 17:03:55

标签: python closures decorator memoization python-decorators

我试图了解Python装饰器,并且我想更详细地了解闭包是如何应用的,例如在这个memoization上下文中:

def memoize(f):
    memo = {}
    def helper(x):                 
        if x not in memo:
            memo[x] = f(x)
        return memo[x]
    return helper   

@memoize  
def fib(n):
    if n in (0,1):
        return n
    return fib(n - 1) + fib(n - 2)

我理解memoize返回绑定到memo的封闭范围内helper值的函数,即使程序流不再在该封闭范围内也是如此。因此,如果重复调用memoize,它将根据memo的当前值返回不同的函数序列。我也理解@memoize是一种语法糖,会导致调用fib(n)memoize(fib(n))调用替换。

我正在努力的是nfib(n)的被叫值如何有效地转换为x中的helper(x)。关于闭包的大多数教程似乎都没有明确指出这一点,或者他们相当模糊地说一个函数“关闭”另一个函数,这让它看起来像魔术。我可以看到如何使用语法,但希望更好地掌握这里发生的事情以及代码执行时传递的对象和数据。

1 个答案:

答案 0 :(得分:4)

你误解了@memoize装饰者的所作所为。每次调用fib时都不会调用装饰器。装饰器每个函数只被称为

原始fib函数由helper()函数替换,原始函数对象可用作f闭包,以及{{1闭包:

memo

这使得替换>>> def fib(n): ... if n in (0,1): ... return n ... return fib(n - 1) + fib(n - 2) ... >>> fib <function fib at 0x1023398c0> >>> memoize(fib) <function helper at 0x102339758> 函数一个闭包,每次调用helper函数时,使用相同的helper()字典来查找 - 生成了当前值memo的结果。

你可以在解释器中看到这一切:

x

注意那里的功能名称!那是>>> @memoize ... def fib(n): ... if n in (0,1): ... return n ... return fib(n - 1) + fib(n - 2) ... >>> fib <function helper at 0x10232ae60> 。它有一个闭包:

helper

一个函数对象和一个字典,分别是>>> fib.__closure__ (<cell at 0x10232d9f0: function object at 0x10232ade8>, <cell at 0x10232da98: dict object at 0x102353050>) f。使用memo

访问单元格内容
.cell_contents

原来的装饰功能和空字典就好了。让我们使用这个功能:

>>> fib.__closure__[0].cell_contents
<function fib at 0x10232ade8>
>>> fib.__closure__[1].cell_contents
{}

>>> fib(0) 0 >>> fib(1) 1 >>> fib.__closure__[1].cell_contents {0: 0, 1: 1} 设置为x0的结果值存储在1字典中。下次我使用任一值调用memo时,将使用备忘录。计算并添加新的fib值:

x

您可以通过计算更大数字计算的时间来了解其效果如何:

>>> fib(2)
1
>>> fib.__closure__[1].cell_contents
{0: 0, 1: 1, 2: 1}

第一次通话后,查找备忘录的速度是所有中间结果记忆的40倍:

>>> start = time.clock(); fib(500); print(format(time.clock() - start, '.10f'))
139423224561697880139724382870407283950070256587697307264108962948325571622863290691557658876222521294125L
0.0008390000
>>> start = time.clock(); fib(500); print(format(time.clock() - start, '.10f'))
139423224561697880139724382870407283950070256587697307264108962948325571622863290691557658876222521294125L
0.0000220000

>>> start = time.clock(); fib(500); print(format(time.clock() - start, '.10f'))
139423224561697880139724382870407283950070256587697307264108962948325571622863290691557658876222521294125L
0.0000190000