【问题标题】:implementing python generators with closures用闭包实现 python 生成器
【发布时间】:2012-02-20 01:38:41
【问题描述】:

我怎样才能摆脱 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)

【问题讨论】:

  • “我不想使用原生生成器……”如果你在做这种事情,大概是因为对函数式编程感兴趣;在这种情况下,您应该意识到使用生成器可以避免在生成器实例之间共享状态。

标签: python lambda closures


【解决方案1】:

在 Python 3.x 中,您可以使用 nonlocal statement:

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

这种不令人满意的情况正是在 Python 3.x 中引入 nonlocal 的原因。

Python 没有变量声明,所以它必须自己弄清楚每个变量的作用域。它通过一个简单的规则来做到这一点:如果在函数内部对名称进行了赋值,则该名称是该函数的本地名称——除非它显式声明为globalnonlocal。在第二个示例中,名称ab 没有分配——列表被修改,但名称没有重新分配。因此作用域是封闭函数。

【讨论】:

  • 这种行为背后的原因是什么?我有点困惑为什么会这样。
  • @DustinGetz:添加了一些解释。
  • Python 实际上只有局部和全局作用域,没有词法作用域。分配给变量有效地将其声明为本地,除非另有明确标记。在 2.x 中,您不能“重新分配”给非局部、非全局变量,因为它不是全局变量(因此您不能标记它),并且赋值会创建一个新的局部变量。 hack 通过改变 ab 变量包含的对象而不是尝试替换它来避免这种情况。
【解决方案2】:

如果我真的必须避免使用生成器,我可能会这样做:

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】:

    这是一种作弊,所以希望有人能给你一个更好的答案,但是:

    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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-17
      • 1970-01-01
      • 2020-06-10
      • 1970-01-01
      • 2022-01-17
      • 2011-12-19
      • 2015-06-08
      相关资源
      最近更新 更多