来自不太动态的 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]
,解释器会尝试查找局部变量,因为它无法更改全局变量。
令人困惑的是,对列表使用+=
运算符会修改原始列表,如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]
的含义相同。
关于python - 在 Python 中写入(而不是)全局变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20771487/