3290 Stimmen

Wie übergebe ich eine Variable per Referenz?

Werden die Parameter als Referenz oder als Wert übergeben? Wie übergebe ich per Referenz, so dass der folgende Code Folgendes ausgibt 'Changed' anstelle von 'Original' ?

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

32 Stimmen

Eine kurze Erklärung/Klarstellung finden Sie in der ersten Antwort auf diese Stackoverflow-Frage . Da Zeichenketten unveränderlich sind, werden sie nicht geändert und es wird eine neue Variable erstellt, so dass die "äußere" Variable immer noch denselben Wert hat.

11 Stimmen

Der Code in BlairConrads Antwort ist gut, aber die Erklärung von DavidCournapeau und DarenThomas ist richtig.

79 Stimmen

Bevor Sie die ausgewählte Antwort lesen, sollten Sie diesen kurzen Text lesen Andere Sprachen haben "Variablen", Python hat "Namen". . Denken Sie an "Namen" und "Objekte" anstelle von "Variablen" und "Referenzen" und Sie sollten viele ähnliche Probleme vermeiden.

104voto

Lutz Prechelt Punkte 32254

In Python gibt es keine Variablen

Der Schlüssel zum Verständnis der Parameterübergabe liegt darin, dass man aufhört, an "Variablen" zu denken. In Python gibt es Namen und Objekte, und zusammen wirken sie erscheinen wie Variablen, aber es ist sinnvoll, immer zwischen diesen drei zu unterscheiden.

  1. Python hat Namen und Objekte.
  2. Die Zuweisung bindet einen Namen an ein Objekt.
  3. Durch die Übergabe eines Arguments an eine Funktion wird auch ein Name (der Parametername der Funktion) an ein Objekt gebunden.

Das ist alles, was es zu sagen gibt. Die Wandlungsfähigkeit ist für diese Frage irrelevant.

Ejemplo:

a = 1

Dies bindet den Namen a auf ein Objekt vom Typ Integer, das den Wert 1 enthält.

b = x

Dies bindet den Namen b auf das gleiche Objekt, das der Name x ist derzeit gebunden an. Danach wird der Name b hat nichts mit dem Namen zu tun x mehr.

Siehe Abschnitte 3.1 y 4.2 in der Python 3 Sprachreferenz.

Wie ist das Beispiel in der Frage zu lesen?

In dem in der Frage gezeigten Code ist die Anweisung self.Change(self.variable) bindet den Namen var (im Rahmen der Funktion Change ) zu dem Objekt, das den Wert 'Original' und die Zuordnung var = 'Changed' (im Körper der Funktion Change ) weist denselben Namen erneut zu: einem anderen Objekt (das zufällig auch eine Zeichenkette enthält, aber auch etwas ganz anderes hätte sein können).

Übergabe durch Referenz

Wenn es sich bei dem zu ändernden Objekt also um ein veränderbares Objekt handelt, gibt es kein Problem, da alles per Referenz übergeben wird.

Wenn es sich um eine unveränderlich Objekt (z.B. ein bool, eine Zahl, eine Zeichenkette) in ein veränderbares Objekt zu verpacken, ist der richtige Weg.
Die schnelle und einfache Lösung hierfür ist eine Ein-Element-Liste (anstelle von self.variable , passen [self.variable] und in der Funktion Ändern var[0] ).
Die mehr pythonisch Ansatz wäre die Einführung einer trivialen Klasse mit nur einem Attribut. Die Funktion erhält eine Instanz der Klasse und manipuliert das Attribut.

53 Stimmen

"Python hat keine Variablen" ist ein dummer und verwirrender Slogan, und ich wünschte wirklich, die Leute würden aufhören, ihn zu sagen... :( Der Rest dieser Antwort ist gut!

23 Stimmen

Es mag schockierend sein, aber es ist nicht dumm. Und ich denke auch nicht, dass es verwirrend ist: Es öffnet hoffentlich den Geist des Empfängers für die Erklärung, die auf ihn zukommt, und versetzt ihn in eine nützliche "Ich frage mich, was sie anstelle von Variablen haben"-Haltung. (Ja, das mag bei Ihnen anders sein.)

26 Stimmen

Würden Sie auch sagen, dass Javascript keine Variablen hat? Sie funktionieren genauso wie die von Python. Auch Java, Ruby, PHP, .... Ich denke, eine bessere Lehrmethode wäre: "Die Variablen von Python funktionieren anders als die von C."

88voto

Raymond Hettinger Punkte 197261

Effbot (alias Fredrik Lundh) hat den Stil der Variablenübergabe in Python als call-by-object beschrieben: http://effbot.org/zone/call-by-object.htm

Objekte werden auf dem Heap alloziert und Zeiger auf sie können überall herumgereicht werden.

  • Wenn Sie einen Auftrag erteilen, wie zum Beispiel x = 1000 wird ein Wörterbucheintrag erstellt, der die Zeichenkette "x" im aktuellen Namensraum auf einen Zeiger auf das Integer-Objekt mit der Zahl Tausend abbildet.

  • Wenn Sie "x" aktualisieren mit x = 2000 wird ein neues Integer-Objekt erstellt und das Wörterbuch wird aktualisiert, um auf das neue Objekt zu verweisen. Das alte Tausender-Objekt bleibt unverändert (und kann, je nachdem, ob sich etwas anderes auf das Objekt bezieht, lebendig sein oder nicht).

  • Wenn Sie eine neue Aufgabe erledigen, z. B. y = x wird ein neuer Wörterbucheintrag "y" erstellt, der auf das gleiche Objekt verweist wie der Eintrag für "x".

  • Objekte wie Zeichenketten und Ganzzahlen sind unveränderlich . Das bedeutet einfach, dass es keine Methoden gibt, die das Objekt nach seiner Erstellung ändern können. Wenn zum Beispiel das Integer-Objekt eintausend einmal erstellt wurde, wird es sich nie mehr ändern. Die Mathematik wird durch die Erstellung neuer Integer-Objekte durchgeführt.

  • Objekte wie Listen sind änderbar . Dies bedeutet, dass der Inhalt des Objekts durch alles, was auf das Objekt zeigt, geändert werden kann. Zum Beispiel, x = []; y = x; x.append(10); print y wird gedruckt [10] . Die leere Liste wurde erstellt. Sowohl "x" als auch "y" verweisen auf die gleiche Liste. Die anhängen. Methode verändert (aktualisiert) das Listenobjekt (wie das Hinzufügen eines Datensatzes zu einer Datenbank) und das Ergebnis ist sowohl für "x" als auch für "y" sichtbar (so wie eine Datenbankaktualisierung für jede Verbindung zu dieser Datenbank sichtbar wäre).

Ich hoffe, das klärt das Problem für Sie.

6 Stimmen

Ich bin wirklich dankbar, dass ich dies von einem Entwickler erfahre. Stimmt es, dass die id() Funktion den Wert des Zeigers (Objektreferenz) zurückgibt, wie die Antwort von pepr nahelegt?

7 Stimmen

@HonestAbe Ja, in CPython ist die id() gibt die Adresse zurück. Aber in anderen Pythons, wie PyPy und Jython, wird die id() ist nur ein eindeutiger Objektbezeichner.

68voto

pepr Punkte 18988

Technisch gesehen, Python verwendet immer die Übergabe von Referenzwerten . Ich werde wiederholen meine andere Antwort um meine Aussage zu untermauern.

Python verwendet immer Pass-by-Reference-Werte. Es gibt keine Ausnahme. Jede Variablenzuweisung bedeutet das Kopieren des Referenzwertes. Keine Ausnahme. Jede Variable ist der Name, der an den Referenzwert gebunden ist. Immer.

Sie können sich einen Referenzwert als die Adresse des Zielobjekts vorstellen. Die Adresse wird bei Verwendung automatisch dereferenziert. Wenn Sie mit dem Referenzwert arbeiten, scheint es, als ob Sie direkt mit dem Zielobjekt arbeiten. Aber es ist immer ein Verweis dazwischen, ein Schritt mehr, um zum Ziel zu springen.

Hier ist das Beispiel, das beweist, dass Python die Übergabe durch Referenz verwendet:

Illustrated example of passing the argument

Wenn das Argument als Wert übergeben wurde, wird die äußere lst konnten nicht geändert werden. Grün sind die Zielobjekte (schwarz ist der darin gespeicherte Wert, rot ist der Objekttyp), gelb ist der Speicher mit dem darin befindlichen Referenzwert - gezeichnet als Pfeil. Der blaue durchgehende Pfeil ist der Referenzwert, der an die Funktion übergeben wurde (über den gestrichelten blauen Pfeilpfad). Das hässliche Dunkelgelb ist das interne Wörterbuch. (Es könnte eigentlich auch als grüne Ellipse gezeichnet werden. Die Farbe und die Form sagen nur, dass es intern ist).

Sie können die id() eingebaute Funktion, um den Referenzwert (d.h. die Adresse des Zielobjekts) zu erfahren.

In kompilierten Sprachen ist eine Variable ein Speicherbereich, der den Wert des Typs aufnehmen kann. In Python ist eine Variable ein Name (intern als String erfasst), der an die Referenzvariable gebunden ist, die den Referenzwert des Zielobjekts enthält. Der Name der Variablen ist der Schlüssel im internen Wörterbuch, der Wertteil dieses Wörterbucheintrags speichert den Referenzwert für das Ziel.

Referenzwerte sind in Python verborgen. Es gibt keinen expliziten Benutzertyp für die Speicherung des Referenzwertes. Sie können jedoch ein Listenelement (oder ein Element in einem beliebigen anderen geeigneten Containertyp) als Referenzvariable verwenden, da alle Container die Elemente auch als Referenzen auf die Zielobjekte speichern. Mit anderen Worten, die Elemente befinden sich nicht innerhalb des Containers, sondern nur die Referenzen auf die Elemente.

2 Stimmen

Dies wird durch den Referenzwert bestätigt. +1 für diese Antwort, obwohl das Beispiel nicht gut war.

46 Stimmen

Die Erfindung einer neuen Terminologie (wie "Übergabe durch Referenzwert" oder "Aufruf durch Objekt" ist nicht hilfreich.) "Aufruf durch (Wert|Referenz|Name)" sind Standardbegriffe. "Referenz" ist ein Standardbegriff. Die Übergabe von Referenzen per Wert beschreibt genau das Verhalten von Python, Java und einer Vielzahl anderer Sprachen, die Standardterminologie verwenden.

6 Stimmen

@cayhorstmann: Das Problem ist, dass Python-Variable hat nicht die gleiche terminologische Bedeutung wie in anderen Sprachen. Hier entlang, Referenzaufruf passt nicht gut hierher. Außerdem, wie wollen Sie genau den Begriff definieren Referenz ? Informell könnte man den Python-Weg leicht als Übergabe der Adresse des Objekts beschreiben. Aber das passt nicht zu einer potenziell verteilten Implementierung von Python.

60voto

AmanicA Punkte 4283

Ein einfacher Trick, den ich normalerweise anwende, besteht darin, ihn einfach in eine Liste zu packen:

def Change(self, var):
    var[0] = 'Changed'

variable = ['Original']
self.Change(variable)      
print variable[0]

(Ja, ich weiß, das kann lästig sein, aber manchmal ist es einfach genug, dies zu tun).

14 Stimmen

+1 für die kleine Textmenge, die den wesentlichen Workaround für das Problem, dass Python kein pass-by-reference hat, beschreibt. (Als Folgekommentar/Frage, die hier genauso gut hinpasst wie irgendwo auf dieser Seite: Es ist mir nicht klar, warum Python kein "ref"-Schlüsselwort wie C# bereitstellen kann, das das Argument des Aufrufers einfach in eine Liste wie diese einpackt und Verweise auf das Argument innerhalb der Funktion als das 0te Element der Liste behandelt).

9 Stimmen

Schön. Um an einem Schiedsrichter vorbeizukommen, fügen Sie [ ]s ein.

39voto

KobeJohn Punkte 7028

(Bearbeiten - Blair hat seine äußerst beliebte Antwort aktualisiert, so dass sie jetzt korrekt ist)

Ich denke, es ist wichtig, darauf hinzuweisen, dass der aktuelle Beitrag mit den meisten Stimmen (von Blair Conrad) zwar in Bezug auf sein Ergebnis richtig ist, aber aufgrund seiner Definitionen irreführend und grenzwertig falsch ist. Es gibt zwar viele Sprachen (wie C), die es dem Benutzer erlauben, entweder per Referenz oder per Wert zu übergeben, aber Python gehört nicht dazu.

Die Antwort von David Cournapeau weist auf die wirkliche Antwort hin und erklärt, warum das Verhalten in Blair Conrads Beitrag korrekt zu sein scheint, während die Definitionen es nicht sind.

In dem Maße, in dem Python eine Wertübergabe ist, sind alle Sprachen eine Wertübergabe, da ein Teil der Daten (sei es ein "Wert" oder eine "Referenz") gesendet werden muss. Das bedeutet jedoch nicht, dass Python eine Wertübergabe in dem Sinne ist, wie ein C-Programmierer sie sich vorstellen würde.

Wenn Sie das Verhalten wissen wollen, ist die Antwort von Blair Conrad in Ordnung. Wenn Sie aber wissen wollen, warum Python weder "pass by value" noch "pass by reference" ist, lesen Sie die Antwort von David Cournapeau.

5 Stimmen

Es ist einfach nicht wahr, dass alle Sprachen nach Werten benannt sind. In C++ oder Pascal (und sicher auch in vielen anderen, die ich nicht kenne), gibt es den Aufruf durch Referenz. Zum Beispiel in C++, void swap(int& x, int& y) { int temp = x; x = y; y = temp; } tauscht die ihr übergebenen Variablen aus. In Pascal verwenden Sie var anstelle von & .

2 Stimmen

Ich dachte, ich hätte schon vor langer Zeit darauf geantwortet, aber ich kann es nicht sehen. Der Vollständigkeit halber: cayhorstmann hat meine Antwort missverstanden. Ich habe nicht gesagt, dass alles nach Wert benannt ist. in den Begriffen, die die meisten Menschen zuerst in Bezug auf C / C++ lernen . Es war einfach so, dass einige Wert übergeben wird (Wert, Name, Zeiger usw.) und dass die in Blairs ursprünglicher Antwort verwendeten Begriffe ungenau waren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X