【问题标题】:A function returns another function. Why the function inside can scope a list from the parent but not a constant?一个函数返回另一个函数。为什么里面的函数可以作用于父列表而不是常量?
【发布时间】:2020-09-21 14:36:44
【问题描述】:

考虑这个函数:

def g():
    x = []
    
    def f():
        x.append([0])
        print(x)
        pass
    return f

调用它:

test = g()
test()

我得到以下输出:

Out: [[0]]

我们可以重新初始化测试函数并多次调用:

test = g()
for i in range(3):
    test()

导致以下输出:

Out: [[0]]
[[0], [0]]
[[0], [0], [0]]

但是,定义如下函数:

def j():
    x = 1
    def f():
        x += 1
        print(x)
        pass
    return f

并称它为:

test = j()
test()

导致错误:

UnboundLocalError: local variable 'x' referenced before assignment

列表似乎在内部函数范围内,而值不在。为什么会这样?

【问题讨论】:

标签: python scoping


【解决方案1】:

@rassar 清楚地解释了原因。这里我给出一个解决方案:

def j():
    x = 1
    def f():
        nonlocal x
        x += 1
        print(x)
        pass
    return f

其实这不是一个简单的问题。尽管+= 看起来像一个有界方法调用,一个就地操作(如果您有其他语言的经验)。但是运行在下面的是x = x.__add__(1)x = x.__iadd__(1)。所以这是一个任务。

【讨论】:

  • 谢谢!为什么赋值表达式在子函数内部不可调用,但方法调用可以?
  • 是因为python混合了变量的创建和使用。也就是说,x = y 可以指“创建一个具有值y 的变量x”或“将值y 分配给现有变量x”。调用方法是确定性的,意思是“使用”。 x = y 也是确定性的,意思是“创造”。但是x += y 是模棱两可的。既有“创造”又有“使用”,Python 认为它们指的是同一个变量。
  • UPD: x = y 也是确定性的,但取决于上下文。当它第一次出现在作用域中时,它的意思是“创造”。所以x += yx = x + y 使用内部范围变量x 而不是外部x。 @deppep
  • @QuarticCat x += y 不是模棱两可的。
【解决方案2】:

这是因为j 使用赋值表达式,而g 使用方法调用。请记住,x += 1 等同于 x = x + 1。如果您将g 更改为:

def g():
    x = []

    def f():
        x += [[0]]
        print(x)
        pass
    return f

你得到:

>>> test = g()
>>> test()
Traceback (most recent call last):
  File "<pyshell#20>", line 1, in <module>
    test()
  File "<pyshell#18>", line 5, in f
    x += [[0]]
UnboundLocalError: local variable 'x' referenced before assignment

【讨论】:

  • 谢谢。为什么赋值表达式在子函数内部不可调用,但方法调用可以?
  • @deppep 赋值编译时将变量标记为local。因此,如果您不希望将其视为本地,则需要使用nonlocalglobal。即使是未嵌套的函数也是如此。
【解决方案3】:

因为,python 编译器知道函数 f 中的变量“x”是不在函数 j 中的局部变量。所以发生了你上面提到的错误。

所以你应该使用非本地的。请参考以下链接。 Accessing parent function's variable in a child function called in the parent using python

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-30
    • 2013-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多