Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/353.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 解释这种高阶函数行为_Python_Decorator - Fatal编程技术网

Python 解释这种高阶函数行为

Python 解释这种高阶函数行为,python,decorator,Python,Decorator,有人能解释为什么版本1和版本2以相同的速度执行吗?我预计版本2、3和4将花费大约相同的时间 def fib(n): return n if n in [0, 1] else fib(n-2)+fib(n-1) def memoize(fn): stored_results = {} def memoized(*args): try: return stored_results[args] except KeyEr

有人能解释为什么版本1和版本2以相同的速度执行吗?我预计版本2、3和4将花费大约相同的时间

def fib(n):
    return n if n in [0, 1] else fib(n-2)+fib(n-1)

def memoize(fn):
    stored_results = {}

    def memoized(*args):
        try:
            return stored_results[args]
        except KeyError:
            #nothing cached
            result = stored_results[args] = fn(*args)
            return result

    return memoized

#version 1 (unmemoized)
print timeit.timeit('fib(35)', 'from __main__ import fib', number=1)
print fib, '\n'

#version 2
memo_fib = memoize(fib)
print timeit.timeit('memo_fib(35)', 'from __main__ import memo_fib', number=1)
print memo_fib, '\n'

#version 3 (wrapped)
fib = memoize(fib)
print timeit.timeit('fib(35)', 'from __main__ import fib', number=1)
print fib, '\n'

#version 4 (w/ decoration line)
@memoize
def fib(n):
    return n if n in [0, 1] else fib(n-2)+fib(n-1)

print timeit.timeit('fib(35)', 'from __main__ import fib', number=1)
结果:

version 1:  4.95815300941
<function fib at 0x102c2b320> 

version 2:  4.94982290268
<function memoized at 0x102c2b410> 

version 3:  0.000107049942017
<function memoized at 0x102c2b488> 

version 4:  0.000118970870972
version 1:4.95815300941
版本2:4.94982290268
版本3:0.000107049942017
版本4:0.0001189707070770972

您的
memoize
函数实际上并没有将
fib
替换为
memo\u fib
,它只是返回一个新函数

这个新函数仍然递归调用原始的、未记忆的
fib

所以,基本上,你只是在回忆最高层


fib
中,对
fib
的递归调用仅使用模块全局名称。(函数基本上与任何其他类型的值没有区别,函数名称与任何其他类型的名称没有区别,因此,如果在模块全局级别定义函数,它就是这样做的。例如,如果您使用
dis.dis(fib)
反汇编字节码,您将在名称
fib
上看到一个
LOAD\u global

因此,简单的解决方法是:

fib = memoize(fib)
或者只需使用
memoize
作为装饰器,使其更难出错

换句话说,你的例子3和4

或者,更简单地说,使用内置的装饰器。(请注意其文档中的第二个示例。)



如果你真的想偷偷摸摸:在函数体中定义
fib
。它最终将引用
fib
作为定义范围中的闭合单元,而不是全局(
LOAD\u DEREF
而不是反汇编中的
LOAD\u global
)。然后,您可以进入该范围并替换它的
fib
,这意味着您的递归函数现在被“秘密地”记忆(实际的全局
fib
没有被记忆,但它递归调用的函数是)和“安全地”(除了通过
fib
本身,没有其他人可以引用闭包单元格).

您的
memoize
函数实际上并没有将
fib
替换为
memo\u fib
,它只是返回一个新函数

这个新函数仍然递归调用原始的、未记忆的
fib

所以,基本上,你只是在回忆最高层


fib
中,对
fib
的递归调用仅使用模块全局名称。(函数基本上与任何其他类型的值没有区别,函数名称与任何其他类型的名称没有区别,因此,如果在模块全局级别定义函数,它就是这样做的。例如,如果您使用
dis.dis(fib)
反汇编字节码,您将在名称
fib
上看到一个
LOAD\u global

因此,简单的解决方法是:

fib = memoize(fib)
或者只需使用
memoize
作为装饰器,使其更难出错

换句话说,你的例子3和4

或者,更简单地说,使用内置的装饰器。(请注意其文档中的第二个示例。)



如果你真的想偷偷摸摸:在函数体中定义
fib
。它最终将引用
fib
作为定义范围中的闭合单元,而不是全局(
LOAD\u DEREF
而不是反汇编中的
LOAD\u global
)。然后,您可以进入该范围并替换它的
fib
,这意味着您的递归函数现在被“秘密地”记忆(实际的全局
fib
没有被记忆,但它递归调用的函数是)和“安全地”(除了通过
fib
本身,没有其他人可以引用闭包单元格).

在第2版中,您使用不同的名称存储了记忆版本,因此您调用fib的次数与第一个版本中的次数相同。您的调用堆栈如下所示:

memo_fib(35)
    fib(35)
        fib(34)
            fib(33)
        fib(33)
等等


因此,在本例中,您实际上没有从备忘录中获得任何好处。

在版本2中,您使用不同的名称存储了备忘录版本,因此您调用fib的次数与第一个版本相同。您的调用堆栈如下所示:

memo_fib(35)
    fib(35)
        fib(34)
            fib(33)
        fib(33)
等等


因此,在这种情况下,您实际上没有从备忘录中获得任何好处。

使用
timeit
模块对代码进行计时。使用
timeit
模块对代码进行计时。