一行中的 Python 多个赋值语句

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32156515/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 11:07:19  来源:igfitidea点击:

Python Multiple Assignment Statements In One Line

pythonvariable-assignment

提问by mwcvitkovic

(Don't worry, this isn't another question about unpacking tuples.)

(别担心,这不是关于解包元组的另一个问题。)

In python, a statement like foo = bar = baz = 5assigns the variables foo, bar, and baz to 5. It assigns these variables from left to right, as can be proved by nastier examples like

在 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

But the python language referencestates that assignment statements have the form

但是python 语言参考指出赋值语句具有以下形式

(target_list "=")+ (expression_list | yield_expression)

and on assignment the expression_listis evaluated first and then the assigning happens.

并在分配时expression_list首先评估,然后分配发生。

So how can the line foo = bar = 5be valid, given that bar = 5isn't an expression_list? How are these multiple assignments on one line getting parsed and evaluated? Am I reading the language reference wrong?

那么该行foo = bar = 5如何有效,因为它bar = 5不是expression_list?如何解析和评估一行上的这些多个分配?我读错了语言参考吗?

采纳答案by mwcvitkovic

All credit goes to @MarkDickinson, who answered this in a comment:

所有功劳都归功于@MarkDickinson,他在评论中回答了这个问题:

Notice the +in (target_list "=")+, which means one or more copies. In foo = bar = 5, there are two (target_list "=")productions, and the expression_listpart is just 5

注意+in (target_list "=")+,这意味着一份或多份副本。在foo = bar = 5,有两个(target_list "=")制作,expression_list部分只是5

All target_listproductions (i.e. things that look like foo =) in an assignment statement get assigned, from left to right, to the expression_liston the right end of the statement, after the expression_listgets evaluated.

赋值语句中的所有target_list产生式(即看起来像 的东西foo =)在评估expression_list后从左到右分配到语句的右端expression_list

And of course the usual 'tuple-unpacking' assignment syntax works within this syntax, letting you do things like

当然,通常的“元组解包”赋值语法在此语法中有效,让您可以执行以下操作

>>> 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

回答by John Coleman

Mark Dickinson explained the syntax of what is happening, but the weird examples involving fooshow that the semantics can be counter-intuitive.

Mark Dickinson 解释了正在发生的事情的语法,但涉及的奇怪例子foo表明语义可能违反直觉。

In C, =is a right-associative operator which returns as a value the RHS of the assignment so when you write x = y = 5, y=5is first evaluated (assigning 5 to yin the process) and this value (5) is then assigned to x.

在 C 中,=是一个右结合运算符,它将赋值的 RHS 作为值返回,因此当您编写x = y = 5,时,y=5首先评估(y在过程中将 5 分配给),然后将该值 (5) 分配给x

Before I read this question, I naively assumed that roughly the same thing happens in Python. But, in Python =isn'tan expression (for example, 2 + (x = 5)is a syntax error). So Python must achieve multiple assignments in another way.

在我阅读这个问题之前,我天真地认为 Python 中也发生了大致相同的事情。但是,在 Python=中不是表达式(例如,2 + (x = 5)是语法错误)。所以Python必须以另一种方式实现多重赋值。

We can disassemble rather than guess:

我们可以拆解而不是猜测:

>>> 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

See thisfor a description of the byte code instructions.

对的字节码指令的描述。

The first instruction pushes 5 onto the stack.

第一条指令将 5 压入堆栈。

The second instruction duplicates it -- so now the top of the stack has two 5s

第二条指令复制它——所以现在栈顶有两个 5

STORE_NAME(name)"Implements name = TOS" according to the byte code documentation

STORE_NAME(name)根据字节码文档“实现名称= TOS”

Thus STORE_Name(x)implements x = 5(the 5 on top of the stack), popping that 5 off the stack as it goes, after which STORE_Name(y)implements y = 5with the other 5 on the stack.

因此STORE_Name(x)实现x = 5(堆栈顶部的 5),在执行过程中将这 5 个从堆栈中弹出,然后与堆栈中的其他 5 个STORE_Name(y)实现y = 5

The rest of the bytecode isn't directly relevant here.

字节码的其余部分与此处没有直接关系。

In the case of foo = foo[0] = [0]the byte-code is more complicated because of the lists but has a fundamentally similar structure. The key observation is that once the list [0]is created and placed on the stack then the instruction DUP_TOPdoesn't place another copyof [0]on the stack, instead it places another referenceto the list. In other words, at that stage the top two elements of the stack are aliases for the same list. This can be seen most clearly in the somewhat simpler case:

foo = foo[0] = [0]字节码的情况下,由于列表而更复杂,但具有基本相似的结构。关键的观察是,一旦名单[0]被创建并在堆栈上放置则指令DUP_TOP不到位的另一个副本[0]堆栈上,而不是它的地方另一个引用到列表中。换句话说,在那个阶段,栈顶的两个元素是同一个列表的别名。这可以在稍微简单的情况下最清楚地看到:

>>> x = y = [0]
>>> x[0] = 5
>>> y[0]
5

When foo = foo[0] = [0]is executed, the list [0]is first assigned to fooand then an alias of the same list is assigned to foo[0]. This is why it results in foobeing a circular reference.

foo = foo[0] = [0]被执行时,该列表[0]被首先分配给foo,然后在同一列表中的一个别名分配给foo[0]。这就是为什么它会导致foo循环引用。

回答by Patrick Maupin

bar = 5is not an expression. The multiple assignment is a separate statement from the assignment statement; the expression is everything to the right of the right-most =.

bar = 5不是表达式。多重赋值是与赋值语句分开的语句;表达式是最右边的所有内容=

A good way to think about it is that the right-most =is the major separator; everything to the right of it happens from left to right, and everything to the left of it happens from left to right as well.

考虑它的一个好方法是最右边的=是主要分隔符;它右边的一切都是从左到右发生的,它左边的一切也是从左到右发生的。

回答by Veedrac

https://docs.python.org/3/reference/simple_stmts.html#grammar-token-assignment_stmt

https://docs.python.org/3/reference/simple_stmts.html#grammar-token-assignment_stmt

An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right.

赋值语句评估表达式列表(请记住,这可以是单个表达式或逗号分隔的列表,后者产生一个元组)并将单个结果对象从左到右分配给每个目标列表。