(别担心,这不是解压元组的另一个问题。)
在python中,像foo = bar = baz = 5这样的语句将变量foo、bar和baz分配给5。
>>> foo[0] = foo = [0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined
>>> foo = foo[0] = [0]
>>> foo
[[...]]
>>> foo[0]
[[...]]
>>> foo is foo[0]
True但是python语言引用声明赋值语句具有以下形式
(target_list "=")+ (expression_list | yield_expression)在赋值时,首先对expression_list进行评估,然后进行赋值。
那么,既然foo = bar = 5不是expression_list,那么行expression_list怎么会是有效的呢?如何对一行中的多个作业进行分析和评估?我看错了语言参考资料吗?
发布于 2015-08-22 16:09:23
所有的功劳都归功于@MarkDickinson,他在评论中回答了这一点:
注意
+中的(target_list "=")+,这意味着一个或多个副本。在foo = bar = 5中,有两个(target_list "=")产品,而expression_list部分只是5
在赋值语句中,赋值语句中的所有target_list产品(即类似于foo =的东西)在对expression_list进行计算之后,都会从左到右分配给语句右侧的expression_list。
当然,通常的‘元组-解包’赋值语法在这个语法中工作,允许您做以下事情
>>> foo, boo, moo = boo[0], moo[0], foo[0] = moo[0], foo[0], boo[0] = [0], [0], [0]
>>> foo
[[[[...]]]]
>>> foo[0] is boo
True
>>> foo[0][0] is moo
True
>>> foo[0][0][0] is foo
True发布于 2015-08-22 14:25:53
Mark解释了正在发生的事情的语法,但是涉及foo的奇怪例子表明语义可能是违反直觉的。
在C中,=是一个右关联运算符,它作为赋值的RHS返回值,所以当您编写x = y = 5时,首先计算y=5 (在过程中将5赋值给y ),然后将这个值(5)赋值给x。
在我阅读这个问题之前,我天真地假设Python中发生了大致相同的事情。但是,在Python中,=不是表达式(例如,2 + (x = 5)是语法错误)。因此Python必须以另一种方式实现多个任务。
我们可以拆解而不是猜测:
>>> import dis
>>> dis.dis('x = y = 5')
1 0 LOAD_CONST 0 (5)
3 DUP_TOP
4 STORE_NAME 0 (x)
7 STORE_NAME 1 (y)
10 LOAD_CONST 1 (None)
13 RETURN_VALUE有关字节码指令的说明,请参见这。
第一条指令将5推到堆栈上。
第二个指令重复它--所以现在堆栈的顶部有两个5s。
根据字节码文档,STORE_NAME(name)“实现名称= TOS”
因此,STORE_Name(x)实现x = 5 (堆栈顶部的5),在堆栈上弹出5,然后STORE_Name(y)与堆栈上的其他5一起实现y = 5。
其余的字节码在这里并不直接相关。
在foo = foo[0] = [0]的例子中,由于列表的存在,字节代码更加复杂,但其结构基本相同。关键的观察是,一旦列表[0]被创建并放在堆栈上,那么指令DUP_TOP就不会在堆栈上放置[0]的另一个副本,而是放置对列表的另一个引用。换句话说,在那个阶段,堆栈的前两个元素是同一个列表的别名。在比较简单的情况下,可以最清楚地看出这一点:
>>> x = y = [0]
>>> x[0] = 5
>>> y[0]
5执行foo = foo[0] = [0]时,首先将列表[0]分配给foo,然后将同一个列表的别名分配给foo[0]。这就是为什么它导致foo是一个循环引用。
发布于 2015-08-22 16:24:42
赋值语句计算表达式列表(请记住,这可以是一个表达式或逗号分隔的列表,后者生成一个元组),并将单个结果对象从左到右分配给每个目标列表。
https://stackoverflow.com/questions/32156515
复制相似问题