为什么分配给空列表而不是空元组是有效的?

时间:2015-04-25 19:55:53

标签: python iterable-unpacking

这出现在a recent PyCon talk

声明

[] = []

没有任何意义,但它也不会引发异常。我觉得这一定是因为拆包规则。您也可以使用列表tuple unpacking,例如,

[a, b] = [1, 2]

做你期望的事。作为逻辑结果,当解包的元素数为0时,这也应该有效,这可以解释为什么分配给空列表是有效的。当您尝试将非空列表分配给空列表时会发生什么,这进一步支持了这一理论:

>>> [] = [1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack

如果元组也是如此,我会对这个解释感到满意。如果我们可以解压缩到包含0个元素的列表,我们也应该能够解包为具有0个元素的元组,不是吗?但是:

>>> () = ()
  File "<stdin>", line 1
SyntaxError: can't assign to ()

似乎解包规则不适用于元组,因为它们用于列表。我想不出对这种不一致的任何解释。这种行为有原因吗?

3 个答案:

答案 0 :(得分:20)

@ user2357112的评论似乎是巧合似乎是正确的。 Python源代码的相关部分位于Python/ast.c

switch (e->kind) {
    # several cases snipped
    case List_kind:
        e->v.List.ctx = ctx;
        s = e->v.List.elts;
        break;
    case Tuple_kind:
        if (asdl_seq_LEN(e->v.Tuple.elts))  {
            e->v.Tuple.ctx = ctx;
            s = e->v.Tuple.elts;
        }
        else {
            expr_name = "()";
        }
        break;
    # several more cases snipped
}
/* Check for error string set by switch */
if (expr_name) {
    char buf[300];
    PyOS_snprintf(buf, sizeof(buf),
                  "can't %s %s",
                  ctx == Store ? "assign to" : "delete",
                  expr_name);
    return ast_error(c, n, buf);
}

tuple明确检查长度不为零并在出现错误时引发错误。 list没有任何此类检查,因此没有例外。

我没有看到任何特殊原因允许在分配给空元组时出现错误,因此允许分配到空列表中,但也许有一些特殊情况我不考虑。我建议这可能是一个(微不足道的)错误,并且两种类型的行为都应该相同。

答案 1 :(得分:12)

当我绊倒好奇的东西时,我决定尝试使用dis弄清楚这里发生了什么:

>>> def foo():
...   [] = []
... 
>>> dis.dis(foo)
  2           0 BUILD_LIST               0
              3 UNPACK_SEQUENCE          0
              6 LOAD_CONST               0 (None)
              9 RETURN_VALUE        
>>> def bar():
...   () = ()
... 
  File "<stdin>", line 2
SyntaxError: can't assign to ()

不知何故,Python 编译器特殊情况下LHS上的空元组。这种差异与the specification不同,后者指出:

  

将对象分配给单个目标按递归方式定义如下。

     

...

     
      
  • 如果目标是括在括号中或方括号中的目标列表:对象必须是一个可迭代的项目数与目标列表中的目标相同,并且其项目从左到右分配,到相应的目标。
  •   

所以看起来你在CPython中发现了一个合法的,但最终无关紧要的错误(2.7.8和3.4.1测试过)。

IronPython 2.6.1表现出相同的区别,但是Jython 2.7b3 +有一个陌生人的行为,() = ()开始声明似乎​​无法结束它。

答案 2 :(得分:3)

“分配到列表”是错误的思考方式。

在所有情况下,你都是解包:Python解释器从三种方式创建一个解包指令来编写它,左侧没有列表或元组(代码由{{代码提供) 3}}):

>>> def f():
...     iterable = [1, 2]
...     a, b = iterable
...     (c, d) = iterable
...     [e, f] = iterable
...
>>> from dis import dis
>>> dis(f)
  2           0 LOAD_CONST               1 (1)
              3 LOAD_CONST               2 (2)
              6 BUILD_LIST               2
              9 STORE_FAST               0 (iterable)

  3          12 LOAD_FAST                0 (iterable)
             15 UNPACK_SEQUENCE          2
             18 STORE_FAST               1 (a)
             21 STORE_FAST               2 (b)

  4          24 LOAD_FAST                0 (iterable)
             27 UNPACK_SEQUENCE          2
             30 STORE_FAST               3 (c)
             33 STORE_FAST               4 (d)

  5          36 LOAD_FAST                0 (iterable)
             39 UNPACK_SEQUENCE          2
             42 STORE_FAST               5 (e)
             45 STORE_FAST               6 (f)
             48 LOAD_CONST               0 (None)
             51 RETURN_VALUE      

如您所见,所有三种陈述完全相同。

现在解压缩的基本原理是:

_iterator = iter(some_iterable)
a = next(_iterator)
b = next(_iterator)
for superfluous_element in _iterator:
    # this only happens if there’s something left
    raise SyntaxError('Expected some_iterable to have 2 elements')

类似地,左侧有更多或更少的名字。

现在正如@blckknght所说:编译器由于某种原因检查左侧是否为空元组并且不允许,但是如果它是空列表则不会。

允许分配0个名称是唯一且合乎逻辑的:为什么不呢?你基本上只断言右边的iterable是空的。这种观点似乎也出现在/u/old-man-prismo @gecko提到的共识中:让我们允许() = iterable