问题
how can i get rid of the globals in fib_gen2? I don't want to use native generators or classes per this gist, this is an academic exercise, though I am interested improvements in any of the implementations.
def ftake(fnext, last):
return [fnext() for _ in xrange(last)]
def fib_gen2():
global a; a = 1
global b; b = 1
def next():
global a; global b;
r = a
a, b = b, a + b
return r
return next
assert [1,1,2,3,5] == ftake(fib_gen2(), 5)
回答1:
In Python 3.x, you can use the nonlocal statement:
def fib_gen2():
a = b = 1
def next():
nonlocal a, b
a, b = b, a + b
return b - a
return next
In Python 2.x, you'll need to use some hack:
def fib_gen2():
ab = [1, 1]
def next():
ab[:] = ab[1], ab[0] + ab[1]
return ab[1] - ab[0]
return next
This unsatisfactory situation was the very reason for the introduction of nonlocal
in Python 3.x.
Python has no variable declarations, so it has to figure out the scope of each variable itself. It does so by a simple rule: If there is an assignment to a name inside a function, this name is local to that function -- except it is explicitly declared global
or nonlocal
. In the second example, there is no assignment to the name ab
-- the list is modified, but the name is not reassigned. Thus the scope is the enclosing function.
回答2:
If I really had to avoid generators, I'd probably do this:
def ftake(fnext, last):
return [fnext() for _ in xrange(last)]
def fib_gen3():
def step():
r = step.a
step.a, step.b = step.b, step.a + step.b
return r
step.a = 1
step.b = 1
return step
>>> ftake(fib_gen3(), 10)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
回答3:
This is kind of cheating so hopefully someone can give you a better answer but:
def ftake(fnext, last):
return [fnext() for _ in xrange(last)]
def fib_gen2():
fib_gen2.a = 1
fib_gen2.b = 1
def next():
r = fib_gen2.a
fib_gen2.a, fib_gen2.b = fib_gen2.b, fib_gen2.a + fib_gen2.b
return r
return next
assert [1,1,2,3,5] == ftake(fib_gen2(), 5)
来源:https://stackoverflow.com/questions/9355179/implementing-python-generators-with-closures