Python:如何捕获在非全局祖先外部作用域中声明的变量?

20

给定:

def f():
    x = 0
    def g():
        h()
    def h():
        x += 1
        print(x)
    g()

>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in f
  File "<stdin>", line 4, in g
  File "<stdin>", line 6, in h
UnboundLocalError: local variable 'x' referenced before assignment
>>>

我该如何使得h能够看到x变量?

谢谢。

编辑

之前应该提到,我正在使用Python 2.7.3。


这对我来说似乎是几乎全球性的... 不好的想法吗?(但仍然是个好问题) - Shep
@Shep:你想让每次f()调用时的x不同,所以全局变量是行不通的。 - Eric O. Lebigot
5个回答

15
你可以将x作为一个函数属性
def f():
    f.x = 0
    def g():
        h()
    def h():
        f.x += 1
        print(f.x)
    g()

此外,在 Python 3 中,您可以使用nonlocal 关键字。


3
如果将其附加到gh,则每次调用f都会得到一个单独的x,而不是一个作为f范围的全局变量。这一点尤为重要,如果gh最终成为闭包(甚至是间接的,例如通过添加为回调函数)。 - user395760

9
在Python 3中,只需使用nonlocal
def f():
    x = 0
    def g():
        h()
    def h():
        nonlocal x
        x += 1
        print(x)
    g()
f()

他使用print()函数,这意味着该用户仅使用Python 3.x。 - Ashwini Chaudhary
值得一提的是,虽然我也同意他正在使用Python 3,但带有一个参数的print()实际上在Python 2上也可以工作。 - jamylak
@AshwiniChaudhary:实际上,在Python 2.x中,print()也是有效的语法。 - Joel Cornett

8

如果你正在使用Python 3,你需要使用nonlocal关键字。在函数 h 的开头添加 nonlocal x。如果你正在使用Python 2.x,则可以通过将x变成一个包含一个元素的列表来实现修改:

def f():
    x = [0]
    def g():
        h()
    def h():
        x[0] += 1
        print x[0]
    g()

f()

0
最简单的方法是使用字典或空类,例如:
class Empty:
    x = 0

def f():
    closure1 = dict(x=0)
    closure2 = Empty()
    def g(): h(x)
    def h(x):
        closure1["x"] += 1
        closure2.x += 1
    g()
    print closure1["x"], closure2.x

虽然已经提供了许多好的解决方案,但它们存在一些特殊情况:

  • 根据Ashwini所说,nonlocal仅适用于Python 3.x
  • 根据ovgolovin所说,函数属性在f被重新定义并通过引用调用时会失败

0

我们不能将 x 作为函数参数来绕过吗?

def f():
    x = 0
    def g():
        h(x)
    def h(x):
        x += 1
        print(x)
    g()

f() 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接