简体   繁体   中英

What happened when I change the variable 'x' to 'fab'?Something about decorator and pass the function variable

I define a Factorial function named fab . I use generator to avoid stack overflow.But something I can't understand came up when I try to write a decorator version, which is more intuitive :

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

Why? I know it has something to do with the same name fab , but why fab(5) works fine? I think when I define fab=TCO(fab) , I actually change the object refered by f in inner to object TCO(fab) . So when fab(5) runs, the gen will never be a generator! Because the inner never returns generator!

I am mad...Why ?

fab=TCO(fab)

statment now makes the variable fab points to the function inner . After that, when

yield fab(n-1,s*n)

is encountered, it is not actually calling the original fab function but the inner function which in turn calls the original fab function. Basically, you are coming out of the generator and entering another function every time. Thats why you are getting maximum recursion limit exceeded error.

I think this would be a good example to explain what happens:

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)

result:

original
new
4

this proves:

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.

Your original version yields a generator which yields a generator which yields a generator, etc. To evaluate that linked list you then use the while loop in you TCO decorator. It all is based on the fact that you are linking generators to avoid a large stack.

This concept is broken when you assign something new to the variable fab . Then yield ing fab(n-1, s*n) in fab is accessing the new fab variable and thus not returning a generator anymore but a value. To compute that value, the stack is used, hence you can get the stack overflow problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM