【问题标题】:Why doesnt Pythons += (plus equals) operator modify variables from inner functions?为什么 Python += (plus equals) 运算符不修改内部函数中的变量?
【发布时间】:2011-07-22 23:29:32
【问题描述】:

我想详细了解为什么这不能按预期工作:

def outer():
    mylist = []
    def inner():
        mylist += [1]

    inner()

outer()

尤其是因为mylist.__iadd__([1]) 工作正常。

【问题讨论】:

  • 您是否有不能使用.extend()的原因?
  • .append(),如果您只是添加一个值。
  • 那些是特定于列表的,有问题的通用解决方案。
  • 每天都有人带着UnboundLocalError...

标签: python list scope


【解决方案1】:

问题在于,当您在函数内分配变量名时,Python 假定您正在尝试创建一个新的局部变量,该变量将在外部范围内屏蔽一个名称相似的变量。由于+= 必须获取 mylist 的值才能对其进行修改,因此它会抱怨,因为尚未定义mylist 的本地版本。 MRAB's answer对语义给出了清晰的解释。

另一方面,当您执行mylist.__iadd__([1]) 时,您并没有在函数内分配新的变量名。您只是使用内置方法来修改已分配的变量名称。只要您不尝试为mylist 分配新值,就不会出现问题。出于同样的原因,如果outer 中的mylist 的定义是mylist = [1],那么mylist[0] = 5 行也可以在inner 内部工作。

但是请注意,如果您尝试在函数中的任何位置为 mylist 分配新值,mylist.__iadd__([1]) 确实会失败:

>>> outer()
>>> def outer():
...     mylist = []
...     def inner():
...         mylist.__iadd__([1])
...         mylist = []
...     inner()
... 
>>> outer()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in outer
  File "<stdin>", line 4, in inner
UnboundLocalError: local variable 'mylist' referenced before assignment

如果您想从包含范围内为变量分配新值,在 3.0+ 中,您可以使用 nonlocal,就像您使用 global 为全局变量分配新值一样范围。所以不要这样:

>>> mylist = []
>>> def inner():
...     global mylist
...     mylist += [1]
... 
>>> inner()
>>> mylist
[1]

你这样做:

def outer():
    mylist = []
    def inner():
        nonlocal mylist
        mylist += [1]
    inner()
    print(mylist)
outer()

【讨论】:

  • 谢谢,很详细。我不知道 Python 会尝试x = x.__iadd__(y)(感谢@MRAB)并假设它只会做x.__iadd__(y)。我还最终重命名了变量;)
【解决方案2】:

如果在函数中绑定了名称(分配了变量),则该名称将被视为本地名称,除非它被声明为全局名称。

因此在inner 中,mylist 是本地的。

当你写x += y时,Python会在运行时尝试:

x = x.__iadd__(y)

如果失败,Python 会尝试:

x = x.__add__(y)

【讨论】:

  • +1,具体语义在这里有用。这也表明__iadd__ 都修改了它所绑定的对象并且 返回了该修改的结果。您不会看到很多“公共”内置方法这样做。
【解决方案3】:

假定分配给的任何变量都属于本地范围。

要分配给你的全局变量:

global var
var = 5

在 Python 2 中你不能完全做到你正在做的事情,但在 Python 3 中你可以做到:

nonlocal mylist
mylist += [1]

Python 2 更改某项或某物的属性的替代方法是

def outer():
    mylist = []
    def inner(mylist = mylist):
        mylist += [1]
    inner()
outer()

如果要替换变量的值,需要:

def outer():
    def setlist(newlist):
        mylist = newlist
    mylist = []
    def inner():
        setlist(['new_list'])
    inner()
outer()

【讨论】:

    【解决方案4】:

    这是因为inner() 中的myList 不引用outer() 中定义的myList,这就是加号等于运算符不起作用的原因。我想到了两种解决方案。

    第一个,将myList 作为参数传递给内部:

    def outer():
        mylist = []
        def inner(someList):
            somelist += [1]
    
        inner(mylist)
    
    outer()
    

    第二种方案是在两个函数外声明myList,然后在两个函数内声明为global

    mylist = []
    def outer():
        global mylist
        mylist = []
        def inner():
            global mylist
            mylist += [1]
    
        inner()
    
    outer()
    

    虽然我会推荐第一个解决方案。

    【讨论】:

    • 但我真的不认为他的问题与关闭有关。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-14
    • 2023-02-04
    • 1970-01-01
    • 1970-01-01
    • 2012-01-10
    • 2014-04-25
    相关资源
    最近更新 更多