是时候拔出来了dis http://docs.python.org/library/dis.html.
>>> import dis
>>> dis.dis(foo)
2 0 LOAD_GLOBAL 0 (Bar)
3 CALL_FUNCTION 0
6 DUP_TOP
7 STORE_FAST 0 (bar)
10 STORE_FAST 1 (initial_bar)
3 13 SETUP_LOOP 32 (to 48)
>> 16 LOAD_GLOBAL 1 (True)
19 POP_JUMP_IF_FALSE 47
4 22 LOAD_GLOBAL 0 (Bar)
25 CALL_FUNCTION 0
28 STORE_FAST 2 (next_bar)
5 31 LOAD_FAST 2 (next_bar)
34 DUP_TOP
35 STORE_FAST 0 (bar)
38 LOAD_FAST 0 (bar)
41 STORE_ATTR 2 (next_bar)
44 JUMP_ABSOLUTE 16
>> 47 POP_BLOCK
6 >> 48 LOAD_FAST 1 (initial_bar)
51 RETURN_VALUE
如果仔细观察,您会发现在关键行(第 5 行,请参阅左侧的数字,位置 31-47)中,它执行以下操作:
- Load
next_bar
(31)两次(34);
- 将其(堆栈上的第一个副本)写入
bar
(35);
- 将其(堆栈上的第二个副本)写入
bar.next_bar
(38、41)。
这在最小的测试用例中看得更明显。
>>> def a():
... b = c = d
...
>>> dis.dis(a)
2 0 LOAD_GLOBAL 0 (d)
3 DUP_TOP
4 STORE_FAST 0 (b)
7 STORE_FAST 1 (c)
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
看看它在做什么。这意味着b = c = d
实际上相当于b = d; c = d
。通常这并不重要,但在最初提到的情况下,它确实很重要。这意味着在临界线上,
bar = bar.next_bar = next_bar
不等于
bar.next_bar = next_bar
bar = next_bar
而是为了
bar = next_bar
bar.next_bar = next_bar
事实上,Python 文档的第 6.2 节对此进行了记录,简单的陈述, 赋值语句 http://docs.python.org/2/reference/simple_stmts.html#assignment-statements:
赋值语句计算表达式列表(请记住,这可以是单个表达式或逗号分隔的列表,后者生成一个元组)并将单个结果对象分配给每个目标列表,从左到右.
该部分还有一个适用于这种情况的相关警告:
警告:尽管赋值的定义意味着左侧和右侧之间的重叠是“安全的”(例如a, b = b, a
交换两个变量),重叠within分配给变量的集合不安全!例如,以下程序打印[0, 2]
:
x = [0, 1]
i = 0
i, x[i] = 1, 2
print x
可以去bar.next_bar = bar = next_bar
这确实产生了最初期望的结果,但对任何人(包括原作者一段时间后!)表示遗憾,他们将不得不稍后阅读代码并为这一事实感到高兴,用我确信蒂姆会使用的语言来说如果他想到了他们,
明确的比可能令人困惑的极端情况要好。