dwa*_*son 5 python methods global operator-keyword
我已经阅读了其他一些SO(PythonScope和全局变量不需要全局),但似乎没有任何内容可以像我想的那样明确解释,而且我在精神上筛选PyDocs是否告诉我问题的答案时遇到了麻烦:
myList = [1]
def foo():
myList = myList + [2, 3]
def bar():
myList.extend([2, 3])
def baz():
myList += [2, 3]
Run Code Online (Sandbox Code Playgroud)
现在,可以理解,
>>> foo()
UnboundLocalError
Run Code Online (Sandbox Code Playgroud)
和
bar() # works
myList # shows [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)
但是之后
>>> baz()
UnboundLocalError
Run Code Online (Sandbox Code Playgroud)
然而,我认为,+=在这种情况下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
Run Code Online (Sandbox Code Playgroud)
所有这些都是预期的,如果+=实际上只是电话extend().
有没有办法,我失踪,使得它清楚地表明了一些更细的区分(或非常明显的一点...)myList在baz()需要被当作一个局部变量,并因此+=不能被隐式转换为extend()使其认识到全球变量?
+=不隐式调用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
Run Code Online (Sandbox Code Playgroud)
增强赋值对目标(与普通赋值语句不同,不能是解包)和表达式列表求值,对两个操作数执行特定于赋值类型的二元运算,并将结果赋给原始目标。目标仅评估一次。
第一次调用尝试评估myList,这会导致LOAD_FAST由于没有global声明它被假定为局部变量:
LOAD_FAST(var_num)将本地 引用推
co_varnames[var_num]入堆栈。
找不到它,因此引发错误。如果找到了,那么我们就到达 oppcode INPLACE_ADD,它调用执行扩展工作的方法myList.__iadd__,一旦此操作完成,结果将被分配回变量,但我们永远不会走到这一步。
无论如何,您不应该真正操作globals,从函数返回新结果或将其作为参数传递。