在Python中写入(而非写入)全局变量
问题内容:
来自不太动态的C ++,我在理解此Python(2.7)代码的行为时遇到了一些麻烦。
注意: 我知道这是不好的编程风格/邪恶,但我希望对此有所了解。
vals = [1,2,3]
def f():
vals[0] = 5
print 'inside', vals
print 'outside', vals
f()
print 'outside', vals
此代码运行无误,并f
操纵(看似)全局列表。这与我先前的理解相反,必须将要在函数中操作(且不仅要读取)的全局变量声明为global ...
。
另一方面,如果我替换vals[0] = 5
为vals += [5,6]
,则UnboundLocalError
除非添加global vals
到,否则执行将失败并显示为f
。这也是我在第一种情况下的预期。
您能解释一下这种行为吗?
为什么vals
在第一种情况下可以操作?为什么第二种类型的操作会失败而第一种类型的却不会失败?
更新: 在vals.extend(...)
没有的情况下,评论中指出了这一点global
。这加剧了我的困惑-
为什么+=
将呼叫与区别对待extend
?
问题答案:
global
仅在尝试更改变量引用的对象时才需要。因为vals[0] = 5
更改的是实际对象而不是参考,所以不会引发任何错误。但是,使用vals += [5, 6]
,解释器会尝试查找局部变量,因为它无法更改全局变量。
令人困惑的是,将+=
运算符与list一起使用会修改原始列表,例如vals[0] = 5
。虽然vals += [5, 6]
失败了,但vals.extend([5, 6])
行得通。我们可以争取到的帮助为dis.dis
我们提供一些线索。
>>> def a(): v[0] = 1
>>> def b(): v += [1]
>>> def c(): v.extend([1])
>>> import dis
>>> dis.dis(a)
1 0 LOAD_CONST 1 (1)
3 LOAD_GLOBAL 0 (v)
6 LOAD_CONST 2 (0)
9 STORE_SUBSCR
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
>>> dis.dis(b)
1 0 LOAD_FAST 0 (v)
3 LOAD_CONST 1 (1)
6 BUILD_LIST 1
9 INPLACE_ADD
10 STORE_FAST 0 (v)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
d
>>> dis.dis(c)
1 0 LOAD_GLOBAL 0 (v)
3 LOAD_ATTR 1 (extend)
6 LOAD_CONST 1 (1)
9 BUILD_LIST 1
12 CALL_FUNCTION 1
15 POP_TOP
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
我们可以看到功能a
和c
用途LOAD_GLOBAL
,而b
尝试使用LOAD_FAST
。现在我们可以看到为什么使用+=
不起作用的原因-
解释器尝试将其v
作为局部变量加载,因为它是就地添加的默认行为。由于它不知道是否v
为列表,因此基本上假设该行的含义与相同v = v + [1]
。