Esiste un metodo standardizzato per scambiare due variabili in Python?


345

In Python, ho visto due valori variabili scambiati usando questa sintassi:

left, right = right, left

Questo è considerato il modo standard di scambiare due valori di variabili o esiste qualche altro mezzo con il quale due variabili vengono generalmente scambiate per convenzione?


1
@eyquem: dipende semplicemente dal fatto che l' ordine di valutazione sia definito dalla lingua per un compito tupla / elenco. Python lo fa, la maggior parte delle lingue meno recenti no.
smci,

Hrmm C ++ ha swap (a [i], a [k]) perché non possiamo avere qualcosa del genere per Python.
Nils,

Risposte:


389

Python valuta le espressioni da sinistra a destra. Si noti che durante la valutazione di un compito, il lato destro viene valutato prima del lato sinistro.

http://docs.python.org/3/reference/expressions.html#evaluation-order

Ciò significa quanto segue per l'espressione a,b = b,a:

  • b,aviene valutato il lato destro , ovvero una tupla di due elementi viene creata nella memoria. I due elementi sono gli oggetti designati dagli identificatori bea , che esistevano prima che l'istruzione venisse rilevata durante l'esecuzione del programma
  • subito dopo la creazione di questa tupla, non è ancora stata assegnata alcuna assegnazione di questo oggetto tupla, ma non importa, Python sa internamente dove si trova
  • quindi, viene valutato il lato sinistro, vale a dire che la tupla è assegnata al lato sinistro
  • poiché il lato sinistro è composto da due identificatori, la tupla viene decompressa in modo che il primo identificatore asia assegnato al primo elemento della tupla (che è l'oggetto che era formalmente b prima dello scambio perché aveva il nome b)
    e il il secondo identificatore bè assegnato al secondo elemento della tupla (che è l'oggetto che in precedenza era uno prima dello scambio perché i suoi identificatori erano a)

Questo meccanismo ha effettivamente scambiato gli oggetti assegnati agli identificatori aeb

Quindi, per rispondere alla tua domanda: SÌ, è il modo standard di scambiare due identificatori su due oggetti.
A proposito, gli oggetti non sono variabili, sono oggetti.


1
Per quanto ho capito, scambiare due variabili in questo modo NON utilizza memoria aggiuntiva, solo memoria per 2 variabili stesse, ho ragione?
Catbuilt

1
@Catbuilts La costruzione della tupla occuperà un po 'di memoria aggiuntiva (probabilmente occuperebbe più della versione a 3 variabili dello swap), ma poiché le uniche cose che vengono scambiate sono indirizzi di memoria, non sarà molta memoria extra in assoluto senso (forse 24 byte extra).
Brilliand,

@Brilliand: Thks. Hai dei documenti per questo argomento. È abbastanza interessante e vorrei avere una lettura più approfondita. Grazie.
Catbuilts

1
@Catbuilts Non ne sono sicuro, ma potrebbe essere utile leggere come funzionano i puntatori C ++. Mentre Python cerca di fare automaticamente le cose nel modo migliore per te, C ++ in realtà ti offre tutte le opzioni per fare le cose in tutti i possibili modi giusti e sbagliati, quindi è un buon punto di partenza per imparare quali sono gli svantaggi dell'approccio che Python adotta . Ricorda inoltre che avere un sistema operativo a "64 bit" significa che la memorizzazione di un indirizzo di memoria occupa 64 bit di memoria - che fa parte di dove ho ottenuto il mio numero "24 byte".
Brilliand,

Ottima spiegazione Basta aggiungere che questo è il motivo per cui puoi anche usare questo metodo per riorganizzare un numero qualsiasi di "variabili", ad es a, b, c = c, a, b.
alexlomba87,


38

Conosco tre modi per scambiare variabili, ma a, b = b, aè il più semplice. C'è

XOR (per numeri interi)

x = x ^ y
y = y ^ x
x = x ^ y

O concisamente,

x ^= y
y ^= x
x ^= y

Variabile temporanea

w = x
x = y
y = w
del w

Scambio di tuple

x, y = y, x

1
È il più semplice e l'unico che non è offuscato.
Jorge Leitao,

17
XOR non scambia "variabili". Scambia variabili intere. (O pochi altri tipi che implementano correttamente l'operatore XOR) Inoltre, poiché secondo la risposta di Rogalski, lo Swap Tuple è ottimizzato nell'interprete, non c'è davvero nulla contro di esso. Breve, chiaro e veloce.
Rawler

Il problema XOR può essere evitato con + - l'uso dell'operatore, ma mi sento ancora meglio a a, b = b, a codex = x + yy = xy x = xycode
ashish

22

Non direi che è un modo standard di scambiare perché causerà alcuni errori imprevisti.

nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]

nums[i]verrà modificato prima e quindi influenzerà la seconda variabile nums[nums[i] - 1].


2
Hai il problema in quasi tutti i linguaggi di programmazione, non è sicuro usare swap (a, b), se a dipende da b o viceversa. Ad esempio, swap (a, b) può essere espansa a: var c=a, a=b, b=c. E quindi, l'ultimo incarico utilizzerà il nuovo valore di aper valutare l'indirizzo di b.
Kai Petzke,

1
nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]. Risolverebbe il problema
Bill Cheng,

2
@JacksonKelley La valutazione del lato destro è sicura. In nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]: Il problema è quando python esegue l'assegnazione sul lato sinistro, nums[i]viene modificato, il che provoca nums[nums[i] - 1]cambiamenti imprevisti. Puoi immaginare all'inizio che vuoi nums[1],nums[2] = nums[2],nums[1], ma dopo la nums[1] = nums[2]corsa non ce l'hai più nums[2] = nums[1], invece hai nums[888] = nums[1].
guo

5

Non funziona con array multidimensionali, poiché qui vengono utilizzati i riferimenti.

import numpy as np

# swaps
data = np.random.random(2)
print(data)
data[0], data[1] = data[1], data[0]
print(data)

# does not swap
data = np.random.random((2, 2))
print(data)
data[0], data[1] = data[1], data[0]
print(data)

Vedi anche Scambia sezioni di matrici Numpy


Questa è davvero una caratteristica speciale (o bug) della libreria numpy.
Kai Petzke,

-1

Per aggirare i problemi spiegati da eyquem , è possibile utilizzare il copymodulo per restituire una tupla contenente copie (invertite) dei valori, tramite una funzione:

from copy import copy

def swapper(x, y):
  return (copy(y), copy(x))

Stessa funzione di un lambda:

swapper = lambda x, y: (copy(y), copy(x))

Quindi, assegnali ai nomi desiderati, in questo modo:

x, y = swapper(y, x)

NOTA: se lo si desidera, è possibile importare / utilizzare deepcopyanziché copy.


qual è il problema che stai cercando di risolvere con la copia?
Hanan Shteingart,

Quelli discussi nel post di eyquem .
LogicalBranch,

1
ma non indica alcun problema, infatti dice "SÌ, è il modo standard di scambiare due identificatori"
Hanan Shteingart,

-2

Puoi combinare tuple e swap XOR : x, y = x ^ x ^ y, x ^ y ^ y

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

x, y = x ^ x ^ y, x ^ y ^ y

print('After swapping: x = %s, y = %s '%(x,y))

o

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

print('After swapping: x = %s, y = %s '%(x ^ x ^ y, x ^ y ^ y))

Usando lambda :

x, y = 10, 20

print('Before swapping: x = %s, y = %s' % (x, y))

swapper = lambda x, y : ((x ^ x ^ y), (x ^ y ^ y))

print('After swapping: x = %s, y = %s ' % swapper(x, y))

Produzione:

Before swapping: x =  10 , y =  20
After swapping: x =  20 , y =  10
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.