реализация генераторов Python с замыканиями - PullRequest
1 голос
/ 20 февраля 2012

как я могу избавиться от глобалов в fib_gen2?Я не хочу использовать собственные генераторы или классы для этого gist , это академическое упражнение, хотя меня интересуют улучшения в любой из реализаций.

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)

Ответы [ 3 ]

9 голосов
/ 20 февраля 2012

В Python 3.x вы можете использовать оператор nonlocal :

def fib_gen2():
    a = b = 1
    def next():
        nonlocal a, b
        a, b = b, a + b
        return b - a
    return next

В Python 2.x вам понадобится взломать:

def fib_gen2():
    ab = [1, 1]
    def next():
        ab[:] = ab[1], ab[0] + ab[1]
        return ab[1] - ab[0]
    return next

Эта неудовлетворительная ситуация послужила причиной введения nonlocal в Python 3.x.

Python не имеет объявлений переменных, поэтому он должен выяснить область действия каждой переменной. Это делается по простому правилу: если в функции есть присвоение имени, это имя является локальным для этой функции - за исключением того, что оно явно объявлено global или nonlocal. Во втором примере нет присвоения имени ab - список изменен, но имя не переназначено. Таким образом, область действия - это ограждающая функция.

1 голос
/ 20 февраля 2012

Это обман, так что, надеюсь, кто-то может дать вам лучший ответ, но:

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)
1 голос
/ 20 февраля 2012

Если бы мне действительно пришлось избегать генераторов, я бы, наверное, сделал это:

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]
...