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

Var*_*rdd 110 python syntax variable-assignment

在python 3.4中,我正在打字

[] = "" 
Run Code Online (Sandbox Code Playgroud)

它工作正常,没有异常.虽然当然[]不等于""事后.

[] = ()
Run Code Online (Sandbox Code Playgroud)

也行得很好.

"" = []
Run Code Online (Sandbox Code Playgroud)

尽管如此提出例外,

() = ""
Run Code Online (Sandbox Code Playgroud)

尽管如此提出例外.发生什么了?

Mar*_*ers 132

你不是在比较平等.你正在分配.

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

foo, bar = 1, 2
Run Code Online (Sandbox Code Playgroud)

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

当你这样做时:

[] = ""
Run Code Online (Sandbox Code Playgroud)

您将序列(空字符串仍为序列)分配给名称列表.

它基本上与做同样的事情:

[foo, bar, baz] = "abc"
Run Code Online (Sandbox Code Playgroud)

你在哪里结束了foo = "a",bar = "b"baz = "c",但更少的字符.

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

请参阅Assignment语句文档:

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

将对象分配给目标列表,可选地括在括号或方括号中,递归地定义如下.

强调我的.

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

起点是认识到这已经存在了很长时间并且是无害的.

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


the*_*eye 36

它遵循文档中的Assignment语句部分规则,

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)
Run Code Online (Sandbox Code Playgroud)

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

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

所以,当你说的时候

[] = ""
Run Code Online (Sandbox Code Playgroud)

"" 是一个可迭代的(任何有效的python字符串都是可迭代的),它正在列表的元素上解包.

例如,

>>> [a, b, c] = "123"
>>> a, b, c
('1', '2', '3')
Run Code Online (Sandbox Code Playgroud)

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

但是,试试这个

>>> [] = "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
Run Code Online (Sandbox Code Playgroud)

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

同样的方式,[a] = ""万一,你有一个空字符串,所以没有什么可以真正解压缩,但你正在解压缩一个变量,这也是不可能的.这就是为什么它抱怨"需要超过0值来解压".

除此之外,正如你所注意到的那样,

>>> [] = ()
Run Code Online (Sandbox Code Playgroud)

也没有抛出错误,因为()是一个空元组.

>>> ()
()
>>> type(())
<class 'tuple'>
Run Code Online (Sandbox Code Playgroud)

当它在空列表中解压缩时,没有任何东西可以解压缩.所以没有错误.


但是,当你这样做的时候

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

正如错误消息所示,您正在尝试分配给字符串文字.这是不可能的.这就是你得到错误的原因.就像在说

>>> 1 = "one"
  File "<input>", line 1
SyntaxError: can't assign to literal
Run Code Online (Sandbox Code Playgroud)

内幕

在内部,此赋值操作将转换为UNPACK_SEQUENCE操作码,

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

在这里,因为字符串是空的,所以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
Run Code Online (Sandbox Code Playgroud)

序列123从右到左解压缩到堆栈中.所以,堆栈的顶部将是1,而下一个将是2,而最后一个将是3.然后它从堆栈顶部逐个指定左侧表达式中的变量.


顺便说一下,在Python中,这就是你可以在同一个表达式中进行多个赋值的方法.例如,

a, b, c, d, e, f = u, v, w, x, y, z
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为右手值用于构造元组,然后它将在左侧值上解压缩.

>>> 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
Run Code Online (Sandbox Code Playgroud)

但是经典的交换技术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
Run Code Online (Sandbox Code Playgroud)