6

我已经阅读了其他一些 SO(PythonScopeglobals 不需要 global),但似乎没有什么能像我想要的那样明确解释,而且我在精神上筛选PyDocs是否告诉我问题的答案时遇到了麻烦:

myList = [1]

def foo():
    myList = myList + [2, 3]
def bar():
    myList.extend([2, 3])
def baz():
    myList += [2, 3]

现在,可以理解的是,

>>> foo()
UnboundLocalError: local variable 'myList' referenced before assignment

bar()  # works
myList # shows [1, 2, 3]

但是之后

>>> baz()
UnboundLocalError: local variable 'myList' referenced before assignment

但是,我认为,+=在这种情况下,隐式称为方法运算符之类的东西extend(),但错误意味着由于某种原因它实际上并未+=视为extends(). 这与 Python 解析应该如何工作一致吗?

我会认为调用等效于方法运算符的函数,它们在所有情况下都是等效的。相反,它似乎将其视为+=实际的赋值运算符。除了,这不完全正确,因为如果我做某事(诚然做作):

myList = range(50000000) # wait a second or two on my laptop before returning
myList += [0]            # returns instantly
myList = myList + [1]    # wait a second or two before returning

所有这些都是预期的,如果+=实际上只是调用extend().

是否有一些更好的区别(或非常明显的点......)我错过了,这清楚地表明myListinbaz()需要被视为局部变量,因此+=不能隐式转换为extend()它可以识别全局变量多变的?

4

2 回答 2

4

+=不会隐式调用extend(). 首先,它是一个增广赋值运算符

如果你看它上面的部分assignment说:

将对象分配给单个目标的递归定义如下。

如果目标是标识符(名称):

如果名称没有出现在当前代码块中的全局语句中:名称绑定到当前本地命名空间中的对象。否则:名称绑定到当前全局命名空间中的对象。

由于增强的分配是:

增强赋值是二元运算和赋值语句在单个语句中的组合:

它遵循相同的规则。如你看到的:

>>> def baz():
        myList += [2, 3]


>>> dis.dis(baz)
  2           0 LOAD_FAST                0 (myList)
              3 LOAD_CONST               1 (2)
              6 LOAD_CONST               2 (3)
              9 BUILD_LIST               2
             12 INPLACE_ADD         
             13 STORE_FAST               0 (myList)
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE  

扩充赋值计算目标(与普通赋值语句不同,不能解包)和表达式列表,对两个操作数执行特定于赋值类型的二元运算,并将结果分配给原始目标。目标只评估一次..

第一次调用尝试评估myList,这导致LOAD_FAST因为没有global声明它被假定为局部变量:

LOAD_FAST(var_num)

将对本地 的引用推co_varnames[var_num]送到堆栈上。

找不到它,因此引发了错误。如果找到了,那么我们就到了调用执行扩展工作的方法的 oppcode 一旦这个操作完成,结果将被分配回变量,但我们从来没有走到这一步。INPLACE_ADDmyList.__iadd__

无论如何,您不应该真正操纵globals,从您的函数返回新结果或将其作为参数传递。

于 2013-04-21T04:21:12.120 回答
0

当你改变列表时,你应该说 global myList。变异我的意思是改变参考。第一个示例和第三个示例基本相同,您只需使用 += 作为简写

myList = [1]

def foo():
    global myList
    myList = myList + [2, 3]
def bar():
    myList.extend([2, 3])
def baz():
    global myList
    myList += [2, 3]

foo()
bar()
baz()
于 2013-04-21T03:22:19.417 回答