Gibt es eine standardisierte Methode, um zwei Variablen in Python auszutauschen?

345

In Python wurden zwei Variablenwerte mit dieser Syntax ausgetauscht:

left, right = right, left

Wird dies als Standardmethode zum Austauschen von zwei Variablenwerten angesehen, oder gibt es andere Mittel, mit denen zwei Variablen üblicherweise vertauscht werden?

WilliamKF
quelle
1
@eyquem: Es kommt einfach darauf an, ob die Reihenfolge der Bewertung durch die Sprache für eine Tupel- / Listenzuweisung definiert wird. Python tut es, die meisten älteren Sprachen nicht.
smci
Hrmm C ++ hat Swap (a [i], a [k]), warum können wir so etwas nicht für Python haben.
Nils

Antworten:

389

Python wertet Ausdrücke von links nach rechts aus. Beachten Sie, dass bei der Auswertung einer Zuordnung die rechte Seite vor der linken Seite ausgewertet wird.

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

Das bedeutet für den Ausdruck Folgendes a,b = b,a:

  • Die rechte Seite b,awird ausgewertet, dh es wird ein Tupel aus zwei Elementen im Speicher erstellt. Die beiden Elemente sind die Objekte, die durch die Bezeichner gekennzeichnet sind bund adie vorhanden waren, bevor der Befehl während einer Programmausführung entdeckt wurde
  • Kurz nach der Erstellung dieses Tupels wurde noch keine Zuordnung dieses Tupelobjekts vorgenommen, aber es spielt keine Rolle, Python weiß intern, wo es sich befindet
  • dann wird die linke Seite ausgewertet, dh das Tupel wird der linken Seite zugeordnet
  • Da die linke Seite aus zwei Bezeichnern besteht, wird das Tupel entpackt, damit der erste Bezeichner adem ersten Element des Tupels zugewiesen wird (das ist das Objekt, das vor dem Tausch formell b war, weil es einen Namen hatte b)
    und dem Der zweite Bezeichner bwird dem zweiten Element des Tupels zugewiesen (dies ist das Objekt, das früher ein vor dem Tausch war, weil seine Bezeichner waren a).

Dieser Mechanismus hat die den Bezeichnern aund zugewiesenen Objekte effektiv ausgetauschtb

Um Ihre Frage zu beantworten: JA, es ist die Standardmethode, zwei Bezeichner gegen zwei Objekte auszutauschen.
Die Objekte sind übrigens keine Variablen, sondern Objekte.

eyquem
quelle
1
Soweit ich weiß, verbraucht das Austauschen von zwei Variablen auf diese Weise KEINEN zusätzlichen Speicher, sondern nur Speicher für zwei Variablen selbst, habe ich Recht?
Catbuilts
1
@Catbuilts Das Erstellen des Tupels benötigt etwas zusätzlichen Speicher (wahrscheinlich mehr als die 3-Variablen-Version des Swaps), aber da nur Speicheradressen ausgetauscht werden, wird es absolut nicht viel zusätzlichen Speicher geben Sinn (vielleicht 24 zusätzliche Bytes).
Brilliand
@ Brilliand: Thks. Haben Sie Dokumente zu diesem Thema? Es ist ziemlich interessant und ich würde gerne weiter lesen. Vielen Dank.
Catbuilts
1
@Catbuilts Ich bin mir nicht sicher, aber es könnte hilfreich sein, sich über die Funktionsweise von C ++ - Zeigern zu informieren. Während Python versucht, die Dinge automatisch auf die beste Art und Weise für Sie zu erledigen, bietet Ihnen C ++ tatsächlich alle Optionen, um die Dinge auf die richtigen und falschen Arten zu erledigen. Dies ist also ein guter Ausgangspunkt, um zu lernen, welche Nachteile der Ansatz von Python hat . Denken Sie auch daran, dass ein "64-Bit" -Betriebssystem bedeutet, dass das Speichern einer Speicheradresse 64 Bit Speicherplatz beansprucht - ein Teil davon ist, woher ich meine "24-Byte" -Nummer habe.
Brilliand
Tolle Erklärung. Wenn Sie nur hinzufügen, dass Sie diese Methode auch verwenden können, um eine beliebige Anzahl von "Variablen" neu anzuordnen, z a, b, c = c, a, b.
alexlomba87
109

Das ist die Standardmethode, um zwei Variablen auszutauschen, ja.

Martijn Pieters
quelle
38

Ich kenne drei Möglichkeiten, Variablen auszutauschen, aber a, b = b, adie einfachste. Es gibt

XOR (für ganze Zahlen)

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

Oder kurz gesagt,

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

Temporäre Variable

w = x
x = y
y = w
del w

Tupeltausch

x, y = y, x
Niemand ist hier
quelle
1
Ist die einfachste und einzige, die nicht verschleiert ist.
Jorge Leitao
17
Das XOR tauscht keine "Variablen" aus. Es tauscht ganzzahlige Variablen aus. (Oder die wenigen anderen Typen, die den XOR-Operator ordnungsgemäß implementieren.) Da laut Rogalskis Antwort der Tuple-Swap im Interpreter optimiert ist, ist wirklich nichts dagegen. Kurz, klar und schnell.
Rawler
XOR-Probleme können durch die Verwendung von + - Operatoren vermieden werden, aber ich codecode
denke
22

Ich würde nicht sagen, dass dies eine Standardmethode zum Tauschen ist, da dies zu unerwarteten Fehlern führen wird.

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

nums[i]wird zuerst geändert und wirkt sich dann auf die zweite Variable aus nums[nums[i] - 1].

Yi Huang
quelle
2
Sie haben das Problem in fast jeder Programmiersprache, dh die Verwendung von Swap (a, b) ist nicht sicher, wenn a von b abhängt oder umgekehrt. Zum Beispiel swap (a, b) könnte erweitert werden: var c=a, a=b, b=c. Und dann verwendet die letzte Zuweisung den neuen Wert von a, um die Adresse von b zu bewerten.
Kai Petzke
1
nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]. Würde das Problem lösen.
Bill Cheng
1
Dies löst das Problem, aber warum?
Jackson Kelley
2
@JacksonKelley Die Bewertung der rechten Seite ist sicher. In nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]: Das Problem ist, wenn Python die Zuweisung auf der linken Seite vornimmt, nums[i]geändert wird, was zu nums[nums[i] - 1]unerwarteten Änderungen führt. Du kannst dir vorstellen , dass du es zuerst willst nums[1],nums[2] = nums[2],nums[1], aber nach dem nums[1] = nums[2]Laufen hast du es nicht mehr nums[2] = nums[1], stattdessen hast du es nums[888] = nums[1].
Guo
5

Funktioniert nicht für mehrdimensionale Arrays, da hier Referenzen verwendet werden.

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)

Siehe auch Teile von Numpy-Arrays austauschen

Gizzmole
quelle
Dies ist in der Tat eine Besonderheit (oder ein Fehler) der Numpy-Bibliothek.
Kai Petzke
-1

Um die durch eyquem erklärten Probleme zu umgehen , können Sie mit dem copyModul über eine Funktion ein Tupel zurückgeben, das (umgekehrte) Kopien der Werte enthält:

from copy import copy

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

Gleiche Funktion wie lambda:

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

Weisen Sie diese dann den gewünschten Namen zu:

x, y = swapper(y, x)

HINWEIS: Wenn Sie möchten, können Sie deepcopystattdessen importieren / verwenden copy.

LogicalBranch
quelle
Was ist das Problem, das Sie durch Kopieren lösen möchten?
Hanan Shteingart
Diejenigen, die in Eyquems Beitrag besprochen wurden .
LogicalBranch
1
aber es gibt kein Problem an, tatsächlich sagt er "JA, es ist die Standardmethode, um zwei Bezeichner
auszutauschen
-2

Sie können Tupel- und XOR- Swaps kombinieren : 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))

oder

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

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

Ausgabe:

Before swapping: x =  10 , y =  20
After swapping: x =  20 , y =  10
U-Betcha
quelle