Domanda

In [55]: a = 5

In [56]: b = 6

In [57]: (a, b) = (b, a)

In [58]: a
Out[58]: 6

In [59]: b
Out[59]: 5

Come funziona internamente questo scambio dei valori di a e b?Sicuramente non sta usando una variabile temporanea.

È stato utile?

Soluzione

Python separa l'espressione del lato destro dall'assegnazione del lato sinistro.Innanzitutto viene valutato il lato destro e il risultato viene memorizzato nello stack, quindi i nomi del lato sinistro vengono assegnati utilizzando codici operativi che accettano valori da di nuovo la pila.

Per assegnamenti di tuple con 2 o 3 elementi, Python utilizza direttamente lo stack:

>>> import dis
>>> def foo(a, b):
...     a, b = b, a
... 
>>> dis.dis(foo)
  2           0 LOAD_FAST                1 (b)
              3 LOAD_FAST                0 (a)
              6 ROT_TWO             
              7 STORE_FAST               0 (a)
             10 STORE_FAST               1 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE        

Dopo i due LOAD_FAST codici operativi (che spingono un valore da una variabile nello stack), rimane la parte superiore dello stack [a, b].IL ROT_TWO codice operativo scambia le prime due posizioni nello stack in modo che lo stack ora abbia [b, a] in cima.Il due STORE_FAST codici operativi quindi prende questi due valori e li memorizza nei nomi sul lato sinistro dell'assegnazione.Il primo STORE_FAST estrae un valore dalla cima dello stack e lo inserisce a, il successivo si apre di nuovo, memorizzando il valore in b.La rotazione è necessaria perché Python garantisce che gli assegnamenti in un elenco di destinazione sul lato sinistro vengano eseguiti da sinistra a destra.

Per un incarico con 3 nomi, ROT_THREE seguito da ROT_TWO viene eseguito per invertire i primi tre elementi nello stack.

Per assegnamenti più lunghi sul lato sinistro, viene creata una tupla esplicita:

>>> def bar(a, b, c, d):
...     d, c, b, a = a, b, c, d
... 
>>> dis.dis(bar)
  2           0 LOAD_FAST                0 (a)
              3 LOAD_FAST                1 (b)
              6 LOAD_FAST                2 (c)
              9 LOAD_FAST                3 (d)
             12 BUILD_TUPLE              4
             15 UNPACK_SEQUENCE          4
             18 STORE_FAST               3 (d)
             21 STORE_FAST               2 (c)
             24 STORE_FAST               1 (b)
             27 STORE_FAST               0 (a)
             30 LOAD_CONST               0 (None)
             33 RETURN_VALUE        

Qui lo stack con [d, c, b, a] viene utilizzato per costruire una tupla (in ordine inverso, BUILD_TUPLE esce di nuovo dallo stack, spingendo la tupla risultante nello stack), e poi UNPACK_SEQUENCE estrae nuovamente la tupla dallo stack, spinge nuovamente tutti gli elementi dalla tupla nello stack per il STORE_FAST operazioni.

Quest'ultima può sembrare un'operazione dispendiosa, ma il lato destro di un compito può essere qualcosa di completamente diverso, una chiamata di funzione così produce una tupla forse, quindi l'interprete Python non fa supposizioni e usa il file UNPACK_SEQUENCE codice operativo sempre.Lo fa anche per le operazioni di assegnazione di due e tre nomi, ma un passaggio successivo di ottimizzazione (spioncino). sostituisce a BUILD_TUPLE / UNPACK_SEQUENCE combinazione con 2 o 3 argomenti con quanto sopra ROT_TWO E ROT_THREE codici operativi per l'efficienza.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top