2016-07-13 51 views
17

Perché il seguente genera un'eccezione, sebbene abbia esito positivo?Tuple: + = l'operatore lancia l'eccezione, ma ha successo?

>>> t = ([1, 2, 3], 4) 
>>> t[0] += [1] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: 'tuple' object does not support item assignment 
>>> t 
([1, 2, 3, 1], 4) 
>>> 
+9

[Perché a_tuple [i] + = ['elemento'] 'genera un'eccezione quando l'aggiunta funziona] (https://docs.python.org/2/faq/programming.html#why-does- a-tuple-i-item-raise-an-exception-when-the-addition-works) –

risposta

17

Trovato la risposta su IRC.

t[0] += [1] è una serie di azioni discrete:

  1. carico t[0]
  2. costruzione di un nuovo elenco con 1 in esso
  3. aggiungendo che [1] a qualunque t[0] è
  4. riassegnazione t[0]

Sembra che x += y è fondamentalmente x = x + y (ma, è vero?)

La parte difficile è che += implica l'assegnazione sia al tuple t e all'elenco t[0]

t[0] += [1] non è letteralmentet[0] = t[0] + [1], è è: t[0] = t[0].__iadd__([1])

Ciò che accade è:

  1. __iadd__ entrambi mutano la lista e la restituiscono. Quindi l'elenco (che è il primo elemento in t) ha già aggiunto 1 ad esso.
  2. La mutazione della tupla viene tentata anche sul posto, ma le tuple sono immutabili, risultando nell'eccezione.

Perché non è visibile in bella vista? Perché un n00b come me si aspetterebbe che t[0] += [1] riescano tutti insieme o falliscano, perché è una linea corta di python. Ma non è sempre così.

+2

Questa è una buona domanda e una buona risposta. +1 da me. Inoltre, per ulteriori informazioni su 'a + = b' vs' a = a + b', vedi [questa risposta] (http://stackoverflow.com/questions/15376509/when-is-ix-different-from-ix -in-python/15376520 # 15376520) (disclaimer - Ho scritto una delle risposte). – mgilson

5

Può anche aiutare a comprendere questo comportamento dando un'occhiata al bytecode con dis.dis.

In[5]: dis('t[0] += [1]') 
    1   0 LOAD_NAME    0 (t) 
       3 LOAD_CONST    0 (0) 
       6 DUP_TOP_TWO 
       7 BINARY_SUBSCR 
       8 LOAD_CONST    1 (1) 
      11 BUILD_LIST    1 
      14 INPLACE_ADD 
      15 ROT_THREE 
      16 STORE_SUBSCR 
      17 LOAD_CONST    2 (None) 
      20 RETURN_VALUE 
  • Il valore di t[0] è posto sulla sommità della pila con BINARY_SUBSCR, che è una lista (mutevole) in questo caso.
  • Il valore nella parte superiore della pila ha += [1] eseguito su di esso con INPLACE_ADD, dove in questo caso la parte superiore della pila fa riferimento all'elenco all'interno della tupla.
  • L'assegnazione del t[0] alla sommità della pila avviene con STORE_SUBSCR, che non riesce qui come t sé è una tupla immutabile, sollevando l'errore dopo l'assegnazione += è già verificato.
2

sviluppatori Python hanno scritto una spiegazione ufficiale sul perché succede qui: https://docs.python.org/2/faq/programming.html#why-does-a-tuple-i-item-raise-an-exception-when-the-addition-works

La versione breve è che + = in realtà fa due cose, uno dopo l'altro:

  1. prendere la cosa a destra e aggiungilo alla variabile a sinistra
  2. Metti il ​​risultato nella variabile a sinistra

In questo caso, il passaggio 1 funziona perché è consentito aggiungere elementi agli elenchi (sono modificabili), ma il passaggio 2 non riesce perché non è possibile inserire materiale in tuple dopo averli creati (le tuple sono immutabili).

In un programma reale, suggerirei di non farlo perché t [0] .extend (['c']) fa esattamente la stessa cosa.

+0

s/tabelle/elenchi / – bhdnx