为什么不给空列表(例如 =“”)分配一个错误?
在Python 3.4中,我打字
[] = ""
它工作正常,没有例外引发。 虽然[]
当然不等于""
。
[] = ()
也工作正常。
"" = []
如预期的那样引发了一个例外,
() = ""
如预期的那样引发了一个例外。 发生什么了?
你没有比较平等。 你正在分配 。
Python允许你分配给多个目标:
foo, bar = 1, 2
将这两个值分别赋值给foo
和bar
。 所有你需要的是右边的一个序列或者可迭代的 ,左边的名字列表或元组。
当你这样做时:
[] = ""
您为空的名称列表分配了一个空序列(空string仍是序列)。
这和本质上是一样的:
[foo, bar, baz] = "abc"
你最终得到foo = "a"
, bar = "b"
和baz = "c"
,但是用更less的字符。
但是,您不能将其分配给string,因此分配左侧的""
从不起作用,并且始终是语法错误。
请参阅作业语句文档 :
赋值语句评估expression式列表(请记住,这可以是单个expression式或逗号分隔列表,后者产生一个元组),并将单个结果对象从左到右分配给每个目标列表。
和
将对象分配给目标列表( 可选地包含在圆括号或方括号中)recursion地定义如下。
强调我的 。
Python不会为空列表抛出语法错误实际上是一个错误! 正式logging的语法不允许有空的目标列表,对于空()
你会得到一个错误。 见bug 23275 ; 它被认为是一个无害的bug:
起点是认识到这已经很长时间了,是无害的。
另请参阅为什么它是有效的分配给一个空的列表,而不是一个空的元组?
它遵循文档中的Assignment语句部分规则,
assignment_stmt ::= (target_list "=")+ (expression_list | yield_expression)
如果
target list
是以逗号分隔的目标列表: 对象必须是与目标列表中的目标相同数目的项目,并且项目从左到右分配给相应的目标。对象必须是与目标列表中的目标相同数量的项目,并且项目从左到右分配给相应的目标。
所以,当你说
[] = ""
""
是一个可迭代的(任何有效的pythonstring都是可迭代的),它被解压到列表的元素上。
例如,
>>> [a, b, c] = "123" >>> a, b, c ('1', '2', '3')
既然你有一个空的string和一个空的列表,没有什么可以解压的。 所以,没有错误。
但是,试试这个
>>> [] = "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"
情况下,您试图在空的variables列表上解压string"1"
。 所以它抱怨“太多的值解包(预期0)”。
同样的方式,在[a] = ""
情况下,你有一个空string,所以没有真正的解压缩,但你打开一个variables,这是不可能的。 这就是为什么它抱怨“需要超过0个值才能解包”。
除此之外,正如你注意到的那样,
>>> [] = ()
也抛出没有错误,因为()
是一个空的元组。
>>> () () >>> type(()) <class 'tuple'>
当它被解压到一个空的列表上时,没有任何东西需要解压。 所以没有错误。
但是,当你这样做
>>> "" = [] File "<input>", line 1 SyntaxError: can't assign to literal >>> "" = () File "<input>", line 1 SyntaxError: can't assign to literal
如错误消息所示,您正试图分配给string文字。 这是不可能的。 这就是为什么你得到错误。 这就像是在说
>>> 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)
在这里,由于string是空的, 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
。 然后从堆栈顶部逐个指定左侧expression式的variables。
顺便说一下,在Python中,这是如何在同一expression式中执行多个赋值的。 例如,
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
和ROT_THREE
指令处理,而不是构造元组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