问题:为什么不分配给空列表(例如[] =“”)错误?

在python 3.4中,我输入

[] = "" 

并且工作正常,不会引发异常。虽然当然[]不等于""事后。

[] = ()

也可以。

"" = []

引发异常,

() = ""

引发异常,但是如预期的那样。发生什么了?

In python 3.4, I am typing

[] = "" 

and it works fine, no Exception is raised. Though of course [] is not equal to "" afterwards.

[] = ()

also works fine.

"" = []

raises an exception as expected though,

() = ""

raises an exception as expected though. So, what’s going on?


回答 0

您不是为了平等而比较。您正在分配

Python允许您分配给多个目标:

foo, bar = 1, 2

将两个值分别分配给foobar。您所需要的只是右侧的序列可迭代,左侧的是名称列表或元组。

当您这样做时:

[] = ""

您分配了一个名称列表序列(空字符串仍然是序列)。

本质上与执行此操作相同:

[foo, bar, baz] = "abc"

在哪里结束foo = "a"bar = "b"以及baz = "c",但字符数减少了。

但是,您无法分配给字符串,因此 ""在的左侧永远不会起作用,并且始终是语法错误。

请参阅赋值语句文档

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

将对象分配给目标列表(可选地用括号或方括号括起来)的方式如下所述。

强调我的

Python不会为空列表引发语法错误实际上是一个错误!正式记录的语法不允许有空的目标列表,并且对于空的目标,()确实会出错。见bug 23275 ; 它被认为是无害的错误:

起点是认识到这种情况已经存在很长时间并且是无害的。

另请参见为什么分配给空列表而不分配给空元组有效吗?

You are not comparing for equality. You are assigning.

Python allows you to assign to multiple targets:

foo, bar = 1, 2

assigns the two values to foo and bar, respectively. All you need is a sequence or iterable on the right-hand side, and a list or tuple of names on the left.

When you do:

[] = ""

you assigned an empty sequence (empty strings are sequences still) to an empty list of names.

It is essentially the same thing as doing:

[foo, bar, baz] = "abc"

where you end up with foo = "a", bar = "b" and baz = "c", but with fewer characters.

You cannot, however, assign to a string, so "" on the left-hand side of an assignment never works and is always a syntax error.

See the Assignment statements documentation:

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.

and

Assignment of an object to a target list, optionally enclosed in parentheses or square brackets, is recursively defined as follows.

Emphasis mine.

That Python doesn’t throw a syntax error for the empty list is actually a bit of a bug! The officially documented grammar doesn’t allow for an empty target list, and for the empty () you do get an error. See bug 23275; it is considered a harmless bug:

The starting point is recognizing that this has been around for very long time and is harmless.

Also see Why is it valid to assign to an empty list but not to an empty tuple?


回答 1

它遵循文档中“ 分配声明”部分的规则,

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

如果target list是用逗号分隔的目标列表:该对象必须是可迭代的,并且具有与目标列表中的目标相同数量的项,并且这些项从左到右分配给相应的目标。

该对象必须是与目标列表中的目标具有相同数量项的序列,并且这些项从左到右分配给相应的目标。

所以,当你说

[] = ""

"" 是可迭代的(任何有效的python字符串都是可迭代的),并且正在列表的元素上解压缩。

例如,

>>> [a, b, c] = "123"
>>> a, b, c
('1', '2', '3')

由于您有一个空字符串和一个空列表,因此无需解压缩。因此,没有错误。

但是,尝试一下

>>> [] = "1"
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: too many values to unpack (expected 0)
>>> [a] = ""
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: need more than 0 values to unpack

在这种[] = "1"情况下,您尝试将字符串解压缩到"1"一个空变量列表中。因此,它抱怨“解压缩的值太多(预期为0)”。

同样,[a] = ""以防万一,您有一个空字符串,因此实际上没有要解压缩的内容,但是您正在通过一个变量解压缩它,这也是不可能的。这就是为什么它抱怨“需要超过0个值才能解包”。

除此之外,您已经注意到,

>>> [] = ()

也不会引发错误,因为()是一个空的元组。

>>> ()
()
>>> type(())
<class 'tuple'>

当将其解压缩到一个空列表中时,没有任何要解压缩的内容。所以没有错误。


但是,当你这样做

>>> "" = []
  File "<input>", line 1
SyntaxError: can't assign to literal
>>> "" = ()
  File "<input>", line 1
SyntaxError: can't assign to literal

如错误消息所述,您正在尝试分配给字符串文字。这是不可能的。这就是为什么您会得到错误。就像在说

>>> 1 = "one"
  File "<input>", line 1
SyntaxError: can't assign to literal

内部构造

在内部,此分配操作将转换为UNPACK_SEQUENCE操作码,

>>> dis(compile('[] = ""', "string", "exec"))
  1           0 LOAD_CONST               0 ('')
              3 UNPACK_SEQUENCE          0
              6 LOAD_CONST               1 (None)

在这里,由于字符串为空,因此将UNPACK_SEQUENCE解压缩0时间。但是当你有这样的事情

>>> dis(compile('[a, b, c] = "123"', "string", "exec"))
  1           0 LOAD_CONST               0 ('123')
              3 UNPACK_SEQUENCE          3
              6 STORE_NAME               0 (a)
              9 STORE_NAME               1 (b)
             12 STORE_NAME               2 (c)
             15 LOAD_CONST               1 (None)
             18 RETURN_VALUE

该序列123从右到左解压到堆栈中。因此,堆栈的顶部为,1下一个为2,最后一个为3。然后,它从堆栈的顶部开始逐一分配左侧表达式中的变量。


顺便说一句,在Python中,这就是您可以在同一表达式中进行多个分配的方式。例如,

a, b, c, d, e, f = u, v, w, x, y, z

之所以可行,是因为使用右手边的值构造一个元组,然后将其拆开到左手边的值上。

>>> dis(compile('a, b, c, d, e, f = u, v, w, x, y, z', "string", "exec"))
  1           0 LOAD_NAME                0 (u)
              3 LOAD_NAME                1 (v)
              6 LOAD_NAME                2 (w)
              9 LOAD_NAME                3 (x)
             12 LOAD_NAME                4 (y)
             15 LOAD_NAME                5 (z)
             18 BUILD_TUPLE              6
             21 UNPACK_SEQUENCE          6
             24 STORE_NAME               6 (a)
             27 STORE_NAME               7 (b)
             30 STORE_NAME               8 (c)
             33 STORE_NAME               9 (d)
             36 STORE_NAME              10 (e)
             39 STORE_NAME              11 (f)
             42 LOAD_CONST               0 (None)
             45 RETURN_VALUE

但是经典的交换技术a, b = b, a使用堆栈顶部的元素旋转。如果只有两个或三个元素,则将使用特殊的ROT_TWOROT_THREE说明来对待它们,而不是构造元组和拆包。

>>> dis(compile('a, b = b, a', "string", "exec"))
  1           0 LOAD_NAME                0 (b)
              3 LOAD_NAME                1 (a)
              6 ROT_TWO
              7 STORE_NAME               1 (a)
             10 STORE_NAME               0 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE

It follows the Assignment statements section rules from the documentation,

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

If the target list is a comma-separated list of targets: The object must be an iterable with the same number of items as there are targets in the target list, and the items are assigned, from left to right, to the corresponding targets.

The object must be a sequence with the same number of items as there are targets in the target list, and the items are assigned, from left to right, to the corresponding targets.

So, when you say

[] = ""

"" is an iterable (any valid python string is an iterable) and it is being unpacked over the elements of the list.

For example,

>>> [a, b, c] = "123"
>>> a, b, c
('1', '2', '3')

Since you have an empty string, and an empty list, there is nothing to unpack. So, no error.

But, try this

>>> [] = "1"
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: too many values to unpack (expected 0)
>>> [a] = ""
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: need more than 0 values to unpack

In the [] = "1" case, you are trying to unpack the string "1" over an empty list of variables. So it complains with “too many values to unpack (expected 0)”.

Same way, in [a] = "" case, you have an empty string, so nothing to unpack really, but you are unpacking it over one variable, which is, again, not possible. That is why it complains “need more than 0 values to unpack”.

Apart from that, as you noticed,

>>> [] = ()

also throws no error, because () is an empty tuple.

>>> ()
()
>>> type(())
<class 'tuple'>

and when it is unpacked over an empty list, there is nothing to unpack. So no error.


But, when you do

>>> "" = []
  File "<input>", line 1
SyntaxError: can't assign to literal
>>> "" = ()
  File "<input>", line 1
SyntaxError: can't assign to literal

as the error message says, you are trying to assign to a string literal. Which is not possible. That is why you are getting the errors. It is like saying

>>> 1 = "one"
  File "<input>", line 1
SyntaxError: can't assign to literal

Internals

Internally, this assignment operation will be translated to UNPACK_SEQUENCE op code,

>>> dis(compile('[] = ""', "string", "exec"))
  1           0 LOAD_CONST               0 ('')
              3 UNPACK_SEQUENCE          0
              6 LOAD_CONST               1 (None)

Here, since the string is empty, UNPACK_SEQUENCE unpacks 0 times. But when you have something like this

>>> dis(compile('[a, b, c] = "123"', "string", "exec"))
  1           0 LOAD_CONST               0 ('123')
              3 UNPACK_SEQUENCE          3
              6 STORE_NAME               0 (a)
              9 STORE_NAME               1 (b)
             12 STORE_NAME               2 (c)
             15 LOAD_CONST               1 (None)
             18 RETURN_VALUE

the sequence 123 is unpacked in to the stack, from right to left. So, the top of the stack would be 1 and the next would be 2 and the last would be 3. Then it assigns from the top of the stack to the variables from the left hand side expression one by one.


BTW, in Python, this is how you can do multiple assignments in the same expression. For example,

a, b, c, d, e, f = u, v, w, x, y, z

this works because, the right hand values are used to construct a tuple and then it will be unpacked over the left hand side values.

>>> dis(compile('a, b, c, d, e, f = u, v, w, x, y, z', "string", "exec"))
  1           0 LOAD_NAME                0 (u)
              3 LOAD_NAME                1 (v)
              6 LOAD_NAME                2 (w)
              9 LOAD_NAME                3 (x)
             12 LOAD_NAME                4 (y)
             15 LOAD_NAME                5 (z)
             18 BUILD_TUPLE              6
             21 UNPACK_SEQUENCE          6
             24 STORE_NAME               6 (a)
             27 STORE_NAME               7 (b)
             30 STORE_NAME               8 (c)
             33 STORE_NAME               9 (d)
             36 STORE_NAME              10 (e)
             39 STORE_NAME              11 (f)
             42 LOAD_CONST               0 (None)
             45 RETURN_VALUE

but the classic swapping technique a, b = b, a uses rotation of elements in the top of the stack. If you have only two or three elements then they are treated with special ROT_TWO and ROT_THREE instructions instead of constructing the tuple and unpacking.

>>> dis(compile('a, b = b, a', "string", "exec"))
  1           0 LOAD_NAME                0 (b)
              3 LOAD_NAME                1 (a)
              6 ROT_TWO
              7 STORE_NAME               1 (a)
             10 STORE_NAME               0 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。