为什么分配给空列表(例如 [] = "")不是错误?

Why isn't assigning to an empty list (e.g. [] = "") an error?

在 python 3.4 中,我正在输入

[] = "" 

它工作正常,没有引发异常。当然 [] 之后不等于 ""

[] = ()

也很好用。

"" = []

虽然按预期引发异常,

() = ""

虽然按预期引发了异常。发生什么了?

它遵循文档中的 Assignment statements section 规则,

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.

所以,当你说

[] = ""

"" 是一个可迭代的(任何有效的 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_TWO and ROT_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

你不是在比较平等。您正在分配

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

foo, bar = 1, 2

将这两个值分别赋值给foobar。您所需要的只是右侧的 sequenceiterable,以及左侧的名称列表或元组。

当你这样做时:

[] = ""

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

本质上和做的是一样的:

[foo, bar, baz] = "abc"

你最终得到 foo = "a"bar = "b"baz = "c",但字符较少。

但是,您不能为字符串赋值,因此赋值左侧的 "" 永远不起作用,并且始终是语法错误。

参见 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.

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

强调我的

Python 没有为空列表抛出语法错误实际上是一个错误!官方记录的语法不允许空的目标列表,对于空的 () 你会得到一个错误。参见bug 23275;它被认为是一个无害的错误:

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

另见