2015-04-25 14 views
29

Questo è arrivato in a recent PyCon talk.Perché è valido assegnare ad una lista vuota ma non ad una tupla vuota?

La dichiarazione

[] = [] 

non fa altro significativo, ma non gettare un'eccezione neanche. Ho la sensazione che questo debba essere dovuto alle regole di spacchettamento. Si può fare con le liste tuple unpacking anche, per esempio,

[a, b] = [1, 2] 

fa quello che ci si aspetterebbe. Come conseguenza logica, anche questo dovrebbe funzionare, quando il numero di elementi da decomprimere è 0, il che spiegherebbe perché l'assegnazione a una lista vuota è valida. Questa teoria è ulteriormente supportata da ciò che accade quando si tenta di assegnare una lista non vuota di una lista vuota:

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

sarei felice con questa spiegazione, se lo stesso sarebbe anche vero per le tuple. Se possiamo decomprimere in una lista con 0 elementi, dovremmo anche essere in grado di decomprimere una tupla con 0 elementi, no? Tuttavia:

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

Sembra disimballaggio regole non vengono applicate per le tuple come lo sono per le liste. Non riesco a pensare a nessuna spiegazione per questa incoerenza. C'è una ragione per questo comportamento?

+3

@ozgur ma il disimballaggio con le tuple funziona: 'a, b = 1, 2' è valido ... –

+0

Non sono sicuro, ma penso che [] = []' non stia disimballando. Tuttavia, sono rimasto sorpreso quando ho visto che sarebbe stato possibile: '[a, b] = [1, 2]'. Invece, vorrei fare 'a, b = (1, 2)' – ozgur

+4

Ho la sensazione che non ci sarà alcun principio interessante al lavoro qui. La migliore risposta sarà probabilmente qualcosa del tipo "Ecco la sezione del generatore di codice dove controlla la validità del LHS di un compito, ed ecco il controllo che cattura'() 'ma lascia' [] 'through". Forse sarà perché '()' è riconosciuto come costante o qualcosa del genere. – user2357112

risposta

19

Il commento di @ user2357112 che questa sembra essere una coincidenza sembra essere corretto. La parte rilevante del codice sorgente Python è in 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 s hanno un controllo esplicito che la lunghezza non è zero e sollevano un errore quando si tratta. list s non hanno alcun controllo, quindi non è stata sollevata alcuna eccezione.

Non vedo alcun motivo particolare per consentire l'assegnazione a una lista vuota quando si tratta di un errore da assegnare a una tupla vuota, ma forse c'è qualche caso speciale che non sto considerando. Suggerirei che questo è probabilmente un bug (banale) e che i comportamenti dovrebbero essere gli stessi per entrambi i tipi.

+0

È perfettamente legale in python 2.7 ad avere una tupla di lunghezza 0. – clj

+6

@clj: Non sul lato sinistro di un compito non lo è. – user2357112

+1

Stavo per pubblicare una risposta simile.Sono rimasto piuttosto sorpreso quando ho trovato il codice e ho visto che si trattava di un controllo specifico per le tuple vuote, piuttosto che di un controllo più generale che si applica alle tuple vuote ma non alle liste vuote. – user2357112

12

ho deciso di provare ad usare dis di capire cosa sta succedendo qui, quando ho inciampato in qualcosa di curioso:

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

In qualche modo il Python compilatore speciali-casi una tupla vuota sul lato sinistro. Questa differenza varia da the specification, che indica:

L'assegnazione di un oggetto a un singolo obiettivo è definita in modo ricorsivo come segue.

...

  • Se il bersaglio è una lista di identificativi racchiusi tra parentesi o parentesi quadre: L'oggetto deve essere un iterabile con lo stesso numero di elementi, come ci sono obiettivi nella lista degli obiettivi, e i suoi oggetti sono assegnati, da sinistra a destra, ai bersagli corrispondenti.

così sembra aver trovato una legittima, anche se in ultima analisi irrilevante, bug nel CPython (2.7.8 e 3.4.1 testati).

IronPython 2.6.1 mostra la stessa differenza, ma Jython 2.7b3 + ha un comportamento estraneo, con () =() che avvia una dichiarazione con apparentemente nessun modo per terminarlo.

+2

[Secondo le specifiche] (https://docs.python.org/3/reference/simple_stmts.html#assignment-statements), dovrebbe rifiutare entrambi '[] = []' e '() = []' . – jfs

+0

Ho accettato la risposta di @ Blckknght perché risponde esattamente alla mia domanda, ma anche questa è fantastica! Non sapevo su '' dis'' prima. – j0ker

2

"Assegnare ad una lista" è il modo sbagliato di pensarci.

In tutti i casi si sta disimballaggio: L'interprete Python crea un'istruzione spacchettamento da tutti e tre i modi di scriverlo, non ci sono liste o tuple coinvolte sul lato sinistro (codice per gentile concessione di /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  

Come potete vedere, tutte e tre le affermazioni sono esattamente le stesse.

Cosa disimballaggio fa ora è fondamentalmente:

_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 per più o meno i nomi sul lato sinistro.

Ora come @blckknght ha detto: Il compilatore per qualche ragione controlla se il lato sinistro è una tupla vuota e non lo consente, ma non se è una lista vuota.

È solo coerente e logico consentire l'assegnazione a 0 nomi: perché no? In pratica, asserisci semplicemente che l'iterabile sul lato destro è vuoto. Tale opinione sembra emergere anche come consenso nel bug report @gecko menzionato: Let's allow () = iterable.

Problemi correlati