我有一些像这样的代码:
def foo():
bar = initial_bar = Bar()
while True:
next_bar = Bar()
bar.next_bar = next_bar
bar = next_bar
return initial_bar
意图是形成Bar
s链,可以遵循链表式。
这一切都非常好;但是通过一些被误导的概念,我想用一条线将它剪下来,将循环结束时的赋值复合成一行。
def foo():
bar = initial_bar = Bar()
while True:
next_bar = Bar()
bar = bar.next_bar = next_bar
return initial_bar
因为bar = bar.next_bar = next_bar
会扩展为bar.next_bar = next_bar
,然后会有效bar = bar.next_bar
。 (除非它没有。)
问题是,这不起作用;返回的“初始栏”没有定义next_bar
。通过回到更明确的两线解决方案,我可以轻松地解决它,但是发生了什么?
答案 0 :(得分:5)
是时候退出dis
。
>>> 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),它会这样做:
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节中记录的Simple statements, Assignment statements:
赋值语句计算表达式列表(请记住,这可以是单个表达式或以逗号分隔的列表,后者产生元组)并将单个结果对象分配给每个目标列表,从左侧开始正确。
该部分还有一个相关的警告适用于这种情况:
警告:尽管赋值的定义意味着左侧和右侧之间的重叠是“安全的”(例如
a, b = b, a
交换两个变量),重叠在分配给变量的集合不安全!例如,以下程序打印[0, 2]
:x = [0, 1] i = 0 i, x[i] = 1, 2 print x
有可能去bar.next_bar = bar = next_bar
,这确实产生了最初期望的结果,但是对任何人(包括原作者一段时间之后!)都有同情心,他们将不得不稍后阅读这些代码并为此事感到高兴用他认为蒂姆会用过的话,他会想到这些,
明确比一个可能令人困惑的角落更好。