将变量'x'更改为"fab"时发生了什么?关于装饰器和传递函数变量的一些东西



我定义了一个名为 fab 的阶乘函数。我使用生成器来避免堆栈溢出。但是当我尝试编写装饰器版本时,出现了一些我无法理解的事情,它更直观:

import types
def TCO(f):    
    def inner(*nkw,**kw):
        gen=f(*nkw,**kw)
        while isinstance(gen,types.GeneratorType):
            gen=gen.next()
        return gen
    return inner

def fab(n,s=1):
    if n<2:
        yield s
    else:
        yield fab(n-1,s*n)

x=TCO(fab)
print x(2500) #this works fine, overcoming the limitation of tail-recursion.
fab=TCO(fab) #now just change the variable name.
print fab(5) #this woks fine.
print fab(2500) #this will raise an error :maximum recursion limit exceeded

为什么?我知道它与同名fab有关,但为什么fab(5)工作正常?我认为当我定义fab=TCO(fab)时,我实际上将f引用的对象更改为对象TCO(fab) inner。所以当 fab(5) 运行时, gen永远不会是生成器!因为inner永远不会返回生成器!

我疯了...为什么?

fab=TCO(fab)

语句现在使变量fab指向函数inner。在那之后,当

yield fab(n-1,s*n)

遇到这种情况,它实际上并不是调用原始fab函数,而是调用inner函数,而函数又调用原始fab函数。基本上,您每次都会从生成器中出来并输入另一个功能。这就是您收到错误maximum recursion limit exceeded的原因。

我认为这将是一个很好的例子来解释发生的事情:

def f(x):
    print 'original'
    if x > 0:
        return f(x-1)
    return 0
g = f
def f(x):
    print 'new'
    return x
print g(5)

结果:

original
new
4

这证明:

1.when g(5) runs, origninal `f` is called, not new `f`.
2.as 5>0, 'return f(x-1)' is executed
3.new `f` is called when `f(x-1)` runs. so the result is 4.

您的原始版本产生一个生成器,该生成器产生一个生成器,该生成器产生一个生成器,依此类推。 要评估该链表,您可以使用装饰器中的while循环TCO。 这一切都是基于您链接生成器以避免大型堆栈的事实。

当您为变量分配新内容时,这个概念被打破了 fab . 然后yield fab(n-1, s*n) fab访问新的fab变量,因此不再返回生成器,而是返回一个值。 要计算该值,将使用堆栈,因此可能会出现堆栈溢出问题。

最新更新