Python+;=与全局变量上的函数内部的.extend()比较
我读过一些其他的SO(和),但似乎没有什么能像我想的那样明确地解释,我在精神上难以筛选是否告诉我问题的答案:Python+;=与全局变量上的函数内部的.extend()比较,python,methods,global,operator-keyword,Python,Methods,Global,Operator Keyword,我读过一些其他的SO(和),但似乎没有什么能像我想的那样明确地解释,我在精神上难以筛选是否告诉我问题的答案: myList = [1] def foo(): myList = myList + [2, 3] def bar(): myList.extend([2, 3]) def baz(): myList += [2, 3] 现在,可以理解 >>> foo() UnboundLocalError: local variable 'myList' r
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
及
但是
>>> baz()
UnboundLocalError: local variable 'myList' referenced before assignment
然而,我认为像+=
这样的东西隐式地调用了方法操作符,在本例中是extend()
,但错误意味着出于某种原因,它实际上没有将+=
视为extends()
。这与Python解析应该如何工作一致吗
我本以为调用与方法操作符等价的函数,它们在所有情况下都是等价的。相反,它似乎将+=
视为实际的赋值运算符。不过,这并不是完全正确的,因为如果我做了什么(当然是做作的):
如果+=
实际上只是调用extend()
,那么所有这些都是预期的
是否有更细微的区别(或非常明显的点…)让我清楚地知道
baz()
中的myList
需要被视为局部变量,因此+=
不能隐式转换为extend()
这样它就可以识别全局变量了?当您改变列表时,应该说是全局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()
+=
不隐式调用extend()
。首先,这是一个问题
如果你看一下作业
一节,它会说:
将对象分配给单个目标的递归定义如下
如果目标是标识符(名称):
如果名称未出现在当前代码块的全局语句中:则名称将绑定到当前本地命名空间中的对象。
否则:名称将绑定到当前全局命名空间中的对象
由于扩充赋值是:
增广赋值是在单个语句中,二进制操作和赋值语句的组合:
它遵循同样的规则。
如你所见:
>>> 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\u FAST
,因为没有global
语句,所以假定它是一个局部变量:
LOAD\u FAST(var\u num)
将对本地co\u varnames[var\u num]
的引用推送到堆栈上
找不到它,因此引发了错误。如果找到了,然后我们进入oppcodeINPLACE\u ADD
,它调用方法myList.\uuu iadd\uuuu
,该方法执行扩展任务,一旦该操作完成,结果将被分配回变量,但我们永远不会走到这一步
无论如何,您不应该真正地操纵
global
s,从函数返回新结果或将其作为参数传递。范围较大的示例表明,我的第一个示例(L=L+M)并不等同于我的第三个示例(L+=M),否则它们都会花费大量时间,如果它们是(主动的)更改引用。显然您是正确的,+=不会更改引用。我已经用“is”操作符检查过了。我的意思是,如果需要调整列表大小,我认为+=/extend()/append()可以隐式更改引用操作符,但是用户/代码不需要更改引用。感谢您发布答案并跟进,我非常感谢!啊,好吧,现在更合理了,谢谢你的解释。我还得多考虑一下如何摆弄反汇编程序,因为熟悉它会对我有所帮助。是的,我在实践中不会这样做——我犯过一次错误,不知道为什么会得到这个输出,所以我很好奇,但后来做了正常的事情,只是把它作为参数传递。谢谢5年后,这又咬了我一口,但方式不同。如果isinstance(其他,numpy.ndarray)
和isinstance(orig,list)
,则orig+=other
失败,并出现numpy错误(操作数无法广播
),但orig.extend(其他)
有效
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()
>>> 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