问题描述
来自动态性较差的C ++,我在理解此Python(2.7)代码的行为时遇到了一些麻烦.
Coming from much less dynamic C++, I have some trouble understanding the behaviour of this Python (2.7) code.
注意:我知道这是不好的编程风格/邪恶,但我希望对此有所了解.
Note: I am aware that this is bad programming style / evil, but I would like to understand it non the less.
vals = [1,2,3]
def f():
vals[0] = 5
print 'inside', vals
print 'outside', vals
f()
print 'outside', vals
此代码运行无错误,并且f
操作(看似)全局列表.这与我先前的理解相反,必须将要在函数中操作(且不仅要读取)的全局变量声明为global ...
.
This code runs without error, and f
manipulates the (seemingly) global list. This is contrary to my prior understanding that global variables that are to be manipulated (and not only read) in a function must be declared as global ...
.
另一方面,如果我将vals[0] = 5
替换为vals += [5,6]
,除非我将global vals
添加到f
,否则执行将失败并显示UnboundLocalError
.我也是在第一种情况下会发生这种情况.
On the other hand, if I replace vals[0] = 5
with vals += [5,6]
, execution fails with an UnboundLocalError
unless I add a global vals
to f
. This is what I would have expected to happen in the first case as well.
您能解释一下这种行为吗?
Could you explain this behaviour?
为什么在第一种情况下可以操纵vals
?为什么第二种类型的操作会失败而第一种类型的却不会失败?
Why can I manipulate vals
in the first case? Why does the second type of manipulation fail while the first does not?
更新:注释中指出,vals.extend(...)
在不使用global
的情况下可以工作.这加剧了我的困惑-为什么+=
的处理方式不同于对extend
的调用?
Update:It was remarked in a comment that vals.extend(...)
works without global
. This adds to my confusion - why is +=
treated differently from a call to extend
?
推荐答案
global
仅在尝试更改变量引用的对象时才需要.因为vals[0] = 5
更改实际对象而不是引用,所以不会引发任何错误.但是,使用vals += [5, 6]
时,解释器会尝试查找局部变量,因为它无法更改全局变量.
global
is only needed when you are trying to change the object which the variable references. Because vals[0] = 5
changes the actual object rather than the reference, no error is raised. However, with vals += [5, 6]
, the interpreter tries to find a local variable because it can't change the global variable.
令人困惑的是,将+=
运算符与list一起使用会修改原始列表,例如vals[0] = 5
.而vals += [5, 6]
失败,而vals.extend([5, 6])
起作用.我们可以争取 dis.dis
的帮助,为我们提供一些帮助线索.
The confusing thing is that using the +=
operator with list modifies the original list, like vals[0] = 5
. And whereas vals += [5, 6]
fails, vals.extend([5, 6])
works. We can enlist the help of dis.dis
to lend us some clues.
>>> 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]
相同.
We can see that functions a
and c
use LOAD_GLOBAL
, whereas b
tries to use LOAD_FAST
. We can see now why using +=
won't work - the interpreter tries to load v
as a local variable because of it's default behaviour with in-place addition. Because it can't know whether v
is a list or not, it essentially assumes that the line means the same as v = v + [1]
.
这篇关于在Python中写入(而非写入)全局变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!