2015-04-25 63 views
29

這是在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

@ozgur但元組拆包做工作:'A,B = 1,2'有效... –

+0

我不知道,但我認爲'[] = []'不拆包。然而,當我看到這樣做是可行的時,我感到驚訝:'[a,b] = [1,2]'。相反,我會做'a,b =(1,2)' – ozgur

+4

我覺得在這裏工作不會有什麼有趣的原理。最好的答案可能是這樣的:「這是代碼生成器的一部分,它檢查一個賦值的LHS的有效性,這裏是檢查'()'但是讓'[]'通過」的檢查。也許這是因爲'()'被識別爲一個常量或其他東西。 – user2357112

回答

19

@ 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 s沒有任何這樣的檢查,所以沒有任何異常提出。

我沒有看到允許分配到一個空列表什麼特別的原因,當它是分配給一個空的元組的錯誤,但也許有,我不是考慮一些特殊情況。我建議這可能是一個(微不足道的)錯誤,並且這兩種類型的行爲應該是相同的。

+0

在python 2.7中,長度爲0的元組是完全合法的。 – clj

+6

@clj:不在任務的左側,它不是。 – user2357112

+1

我剛剛發佈了類似的答案。當我找到代碼並且看到它是對空元組的特定檢查時,我感到非常驚訝,而不是一個更普遍的檢查,它適用於空元組而不是空列表。 – user2357112

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

[根據規範](https://docs.python.org/3/reference/simple_stmts.html#assignment-statements),它應該拒絕'[] = []'和'()= []' 。 – jfs

+0

我接受@ Blckknght的答案,因爲它準確地回答了我的問題,但是這個也很棒!之前不知道「dis」。 – j0ker

2

「分配到一個列表」是考慮了錯誤的方式。

在你拆包所有情況:Python解釋器創建一個從三個方面展開的對象指令來寫,有沒有參與在左側列表或元組(的/u/old-man-prismo代碼提供):

>>> 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') 

Analoguously爲左側或多或少的名字。

現在,作爲@blckknght說:由於某種原因,檢查的編譯器,如果左邊是一個空的元組,並不允許這一點,但如果它不是一個空列表。

這只是一致性和邏輯性,使分配給0名:爲什麼不呢?你基本上只是斷言右側的迭代器是空的。這個意見似乎也出現了,作爲共識在bug report @gecko提到:讓我們允許() = iterable