Wie übergebe ich eine Variable als Referenz?

2628

In der Python-Dokumentation scheint unklar zu sein, ob Parameter als Referenz oder Wert übergeben werden, und der folgende Code erzeugt den unveränderten Wert 'Original'.

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

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

Kann ich etwas tun, um die Variable als tatsächliche Referenz zu übergeben?

David Sykes
quelle
23
Eine kurze Erklärung / Erläuterung finden Sie in der ersten Antwort auf diese Frage zum Stapelüberlauf . Da Zeichenfolgen unveränderlich sind, werden sie nicht geändert und eine neue Variable wird erstellt. Daher hat die "äußere" Variable immer noch den gleichen Wert.
PhilS
10
Der Code in BlairConrads Antwort ist gut, aber die Erklärung von DavidCournapeau und DarenThomas ist korrekt.
Ethan Furman
70
Bevor Sie die ausgewählte Antwort lesen, lesen Sie bitte diesen kurzen Text. 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.
lqc
24
Warum verwendet dies unnötig eine Klasse? Dies ist nicht Java: P
Peter R
2
Eine andere Problemumgehung besteht darin, eine Wrapper-Referenz wie folgt zu erstellen: ref = type ('', (), {'n': 1}) stackoverflow.com/a/1123054/409638
robert

Antworten:

2831

Argumente werden durch Zuweisung übergeben . Es gibt zwei Gründe dafür:

  1. Der übergebene Parameter ist tatsächlich eine Referenz auf ein Objekt (die Referenz wird jedoch als Wert übergeben).
  2. Einige Datentypen sind veränderbar, andere nicht

Damit:

  • Wenn Sie ein veränderbares Objekt an eine Methode übergeben, erhält die Methode einen Verweis auf dasselbe Objekt und Sie können es zur Freude Ihres Herzens mutieren. Wenn Sie jedoch den Verweis in der Methode erneut binden, weiß der äußere Bereich nichts darüber und danach Wenn Sie fertig sind, zeigt die äußere Referenz immer noch auf das ursprüngliche Objekt.

  • Wenn Sie ein unveränderliches Objekt an eine Methode übergeben, können Sie die äußere Referenz immer noch nicht erneut binden und das Objekt nicht einmal mutieren.

Um es noch deutlicher zu machen, lassen Sie uns einige Beispiele nennen.

Liste - ein veränderlicher Typ

Versuchen wir, die Liste zu ändern, die an eine Methode übergeben wurde:

def try_to_change_list_contents(the_list):
    print('got', the_list)
    the_list.append('four')
    print('changed to', the_list)

outer_list = ['one', 'two', 'three']

print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)

Ausgabe:

before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']

Da der übergebene Parameter ein Verweis auf outer_listund keine Kopie davon ist, können wir die Methoden der Mutationsliste verwenden, um ihn zu ändern und die Änderungen im äußeren Bereich wiederzugeben.

Nun wollen wir sehen, was passiert, wenn wir versuchen, die als Parameter übergebene Referenz zu ändern:

def try_to_change_list_reference(the_list):
    print('got', the_list)
    the_list = ['and', 'we', 'can', 'not', 'lie']
    print('set to', the_list)

outer_list = ['we', 'like', 'proper', 'English']

print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)

Ausgabe:

before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']

Da der the_listParameter als Wert übergeben wurde, hatte das Zuweisen einer neuen Liste keine Auswirkung, die der Code außerhalb der Methode sehen konnte. Das the_listwar eine Kopie der outer_listReferenz, und wir hatten the_listauf eine neue Liste outer_listverwiesen , aber es gab keine Möglichkeit zu ändern, wo darauf hingewiesen wurde.

String - ein unveränderlicher Typ

Es ist unveränderlich, daher können wir nichts tun, um den Inhalt der Zeichenfolge zu ändern

Versuchen wir nun, die Referenz zu ändern

def try_to_change_string_reference(the_string):
    print('got', the_string)
    the_string = 'In a kingdom by the sea'
    print('set to', the_string)

outer_string = 'It was many and many a year ago'

print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)

Ausgabe:

before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago

Da der the_stringParameter als Wert übergeben wurde, hatte das Zuweisen einer neuen Zeichenfolge keine Auswirkung, die der Code außerhalb der Methode sehen konnte. Das the_stringwar eine Kopie der outer_stringReferenz, und wir hatten the_stringauf eine neue Zeichenfolge gezeigt, aber es gab keine Möglichkeit zu ändern, wo gezeigt wurde outer_string.

Ich hoffe, das klärt die Dinge ein wenig auf.

BEARBEITEN: Es wurde festgestellt, dass dies nicht die Frage beantwortet, die @David ursprünglich gestellt hat: "Kann ich etwas tun, um die Variable als tatsächliche Referenz zu übergeben?". Lassen Sie uns daran arbeiten.

Wie kommen wir darum herum?

Wie die Antwort von @ Andrea zeigt, können Sie den neuen Wert zurückgeben. Dies ändert nichts an der Art und Weise, wie Dinge übergeben werden, aber Sie können die gewünschten Informationen wieder herausholen:

def return_a_whole_new_string(the_string):
    new_string = something_to_do_with_the_old_string(the_string)
    return new_string

# then you could call it like
my_string = return_a_whole_new_string(my_string)

Wenn Sie die Verwendung eines Rückgabewerts wirklich vermeiden möchten, können Sie eine Klasse erstellen, die Ihren Wert enthält, und ihn an die Funktion übergeben oder eine vorhandene Klasse wie eine Liste verwenden:

def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
    new_string = something_to_do_with_the_old_string(stuff_to_change[0])
    stuff_to_change[0] = new_string

# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)

do_something_with(wrapper[0])

Obwohl dies etwas umständlich erscheint.

Blair Conrad
quelle
165
Dann ist das gleiche in C, wenn Sie "als Referenz" übergeben, übergeben Sie tatsächlich als Wert die Referenz ... Definieren Sie "als Referenz": P
Andrea Ambu
104
Ich bin mir nicht sicher, ob ich Ihre Bedingungen verstehe. Ich war eine Weile nicht im C-Spiel, aber damals, als ich dabei war, gab es keine "Referenzübergabe" - man konnte Dinge übergeben, und es wurde immer als Wert übergeben, also was auch immer in der Parameterliste stand wurde kopiert. Aber manchmal war das Ding ein Zeiger, dem man auf das Stück Speicher folgen konnte (primitiv, Array, Struktur, was auch immer), aber man konnte den Zeiger, der aus dem äußeren Bereich kopiert wurde, nicht ändern - wenn man mit der Funktion fertig war zeigte der ursprüngliche Zeiger immer noch auf dieselbe Adresse. C ++ führte Referenzen ein, die sich anders verhielten.
Blair Conrad
34
@Zac Bowling Ich verstehe nicht wirklich, wie das, was Sie sagen, im praktischen Sinne für diese Antwort relevant ist. Wenn ein Python-Neuling wissen möchte, wie man ref / val übergibt, lautet die Antwort aus dieser Antwort: 1- Sie können die Referenz, die eine Funktion als Argumente erhält, verwenden, um den 'externen' Wert einer Variablen so lange zu ändern da Sie den Parameter nicht neu zuweisen, um auf ein neues Objekt zu verweisen. 2- Wenn Sie einem unveränderlichen Typ zuweisen, wird immer ein neues Objekt erstellt, das den Verweis auf die externe Variable unterbricht.
Cam Jackson
11
@CamJackson, Sie brauchen ein besseres Beispiel - Zahlen sind auch unveränderliche Objekte in Python. Wäre es nicht wahr zu sagen, dass eine Zuweisung ohne Subskription auf der linken Seite der Gleichheit den Namen einem neuen Objekt neu zuweist, unabhängig davon, ob es unveränderlich ist oder nicht? def Foo(alist): alist = [1,2,3]wird nicht den Inhalt der Liste der Anrufer Perspektive ändern.
Mark Ransom
59
-1. Der gezeigte Code ist gut, die Erklärung, wie völlig falsch ist. In den Antworten von DavidCournapeau oder DarenThomas finden Sie korrekte Erklärungen, warum.
Ethan Furman
685

Das Problem ergibt sich aus einem Missverständnis der Variablen in Python. Wenn Sie an die meisten traditionellen Sprachen gewöhnt sind, haben Sie ein mentales Modell dessen, was in der folgenden Reihenfolge passiert:

a = 1
a = 2

Sie glauben, dass dies aein Speicherort ist, in dem der Wert 1gespeichert und dann aktualisiert wird, um den Wert zu speichern 2. So funktioniert das in Python nicht. aBeginnt vielmehr als Referenz auf ein Objekt mit dem Wert 1und wird dann als Referenz auf ein Objekt mit dem Wert neu zugewiesen 2. Diese beiden Objekte können weiterhin koexistieren, obwohl sie asich nicht mehr auf das erste beziehen. Tatsächlich können sie von einer beliebigen Anzahl anderer Referenzen innerhalb des Programms geteilt werden.

Wenn Sie eine Funktion mit einem Parameter aufrufen, wird eine neue Referenz erstellt, die auf das übergebene Objekt verweist. Dies unterscheidet sich von der Referenz, die im Funktionsaufruf verwendet wurde. Daher gibt es keine Möglichkeit, diese Referenz zu aktualisieren und auf a zu verweisen neues Objekt. In Ihrem Beispiel:

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

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

self.variableist eine Referenz auf das String-Objekt 'Original'. Wenn Sie aufrufen Change, erstellen Sie einen zweiten Verweis varauf das Objekt. Innerhalb der Funktion weisen Sie die Referenz vareinem anderen Zeichenfolgenobjekt zu 'Changed', die Referenz self.variableist jedoch separat und ändert sich nicht.

Der einzige Weg, dies zu umgehen, besteht darin, ein veränderliches Objekt zu übergeben. Da sich beide Referenzen auf dasselbe Objekt beziehen, werden alle Änderungen am Objekt an beiden Stellen wiedergegeben.

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'
Mark Ransom
quelle
106
Gute prägnante Erklärung. Ihr Absatz "Wenn Sie eine Funktion aufrufen ..." ist eine der besten Erklärungen, die ich für den eher kryptischen Satz gehört habe: "Python-Funktionsparameter sind Referenzen, die als Wert übergeben werden." Ich denke, wenn Sie diesen Absatz alleine verstehen, macht alles andere nur Sinn und fließt als logische Schlussfolgerung daraus. Dann müssen Sie nur noch wissen, wann Sie ein neues Objekt erstellen und wann Sie ein vorhandenes ändern.
Cam Jackson
3
Aber wie können Sie die Referenz neu zuweisen? Ich dachte, Sie können die Adresse von 'var' nicht ändern, aber Ihre Zeichenfolge "Changed" sollte jetzt in der 'var'-Speicheradresse gespeichert werden. Ihre Beschreibung lässt es so aussehen, als ob "Geändert" und "Original" stattdessen zu verschiedenen Stellen im Speicher gehören, und Sie wechseln einfach 'var' zu einer anderen Adresse. Ist das korrekt?
Glassjawed
9
@Glassjawed, ich denke du bekommst es. "Geändert" und "Original" sind zwei verschiedene Zeichenfolgenobjekte mit unterschiedlichen Speicheradressen, und "var" ändert sich von "Zeigen auf" zu "Zeigen".
Mark Ransom
Die Verwendung der Funktion id () hilft bei der Klärung von Fragen, da dadurch deutlich wird, wann Python ein neues Objekt erstellt (also denke ich jedenfalls).
Tim Richardson
1
@MinhTran im einfachsten Sinne ist eine Referenz etwas, das sich auf ein Objekt "bezieht". Die physische Darstellung davon ist höchstwahrscheinlich ein Zeiger, aber das ist einfach ein Implementierungsdetail. Es ist wirklich ein abstrakter Begriff im Herzen.
Mark Ransom
329

Ich fand die anderen Antworten ziemlich lang und kompliziert, deshalb habe ich dieses einfache Diagramm erstellt, um zu erklären, wie Python mit Variablen und Parametern umgeht. Geben Sie hier die Bildbeschreibung ein

Zenadix
quelle
2
schön, macht es leicht, den subtilen Unterschied zu erkennen, dass es eine Zwischenaufgabe gibt, die für einen zufälligen Betrachter nicht offensichtlich ist. +1
user22866
9
Es spielt keine Rolle, ob A veränderlich ist oder nicht. Wenn Sie B etwas anderes zuweisen, ändert sich A nicht . Wenn ein Objekt veränderbar ist, können Sie es sicher mutieren. Aber das hat nichts mit der direkten Zuordnung zu einem Namen zu tun.
Martijn Pieters
1
@ Martinijn Du hast recht. Ich habe den Teil der Antwort entfernt, in dem die Veränderlichkeit erwähnt wird. Ich denke nicht, dass es jetzt einfacher werden kann.
Zenadix
4
Danke für das Update, viel besser! Was die meisten Leute verwirrt, ist die Zuordnung zu einem Abonnement; zB B[0] = 2vs. direkte Zuordnung , B = 2.
Martijn Pieters
11
"A ist B zugeordnet." Ist das nicht mehrdeutig? Ich denke in gewöhnlichem Englisch kann das entweder A=Boder bedeuten B=A.
Hatschepsut
240

Es ist weder Pass-by-Value noch Pass-by-Reference - es ist Call-by-Object. Siehe dies von Fredrik Lundh:

http://effbot.org/zone/call-by-object.htm

Hier ist ein wichtiges Zitat:

"... Variablen [Namen] sind keine Objekte; sie können nicht durch andere Variablen bezeichnet oder von Objekten referenziert werden."

In Ihrem Beispiel wird beim Aufrufen der ChangeMethode ein Namespace dafür erstellt. und varwird innerhalb dieses Namespace zu einem Namen für das Zeichenfolgenobjekt 'Original'. Dieses Objekt hat dann einen Namen in zwei Namespaces. Als nächstes wird var = 'Changed'an varein neues Zeichenfolgenobjekt gebunden, und daher vergisst der Namespace der Methode 'Original'. Schließlich wird dieser Namespace vergessen und die Zeichenfolge 'Changed'mit.

David Cournapeau
quelle
21
Es fällt mir schwer zu kaufen. Für mich sind die Parameter genau wie Java Zeiger auf Objekte im Speicher, und diese Zeiger werden über den Stapel oder die Register übergeben.
Luciano
10
Das ist nicht wie Java. Einer der Fälle, in denen dies nicht der Fall ist, sind unveränderliche Objekte. Denken Sie an die triviale Funktion Lambda x: x. Wenden Sie dies für x = [1, 2, 3] und x = (1, 2, 3) an. Im ersten Fall ist der zurückgegebene Wert eine Kopie der Eingabe und im zweiten Fall identisch.
David Cournapeau
26
Nein, es ist genau wie Javas Semantik für Objekte. Ich bin mir nicht sicher, was Sie unter "Im ersten Fall ist der zurückgegebene Wert eine Kopie der Eingabe und im zweiten Fall identisch." aber diese Aussage scheint eindeutig falsch zu sein.
Mike Graham
23
Es ist genau das gleiche wie in Java. Objektreferenzen werden als Wert übergeben. Jeder, der anders denkt, sollte den Python-Code für eine swapFunktion anhängen, die zwei Referenzen wie diese a = [42] ; b = 'Hello'; swap(a, b) # Now a is 'Hello', b is [42]
austauschen
24
Es ist genau das gleiche wie Java, wenn Sie Objekte in Java übergeben. Java verfügt jedoch auch über Grundelemente, die durch Kopieren des Werts des Grundelements übergeben werden. Sie unterscheiden sich also in diesem Fall.
Claudiu
174

Denken Sie an Dinge, die durch Zuweisung statt durch Referenz / Wert übergeben werden. Auf diese Weise ist immer klar, was passiert, solange Sie verstehen, was während der normalen Aufgabe passiert.

Wenn Sie also eine Liste an eine Funktion / Methode übergeben, wird die Liste dem Parameternamen zugewiesen. Das Anhängen an die Liste führt dazu, dass die Liste geändert wird. Durch das Neuzuweisen der Liste innerhalb der Funktion wird die ursprüngliche Liste nicht geändert, da:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

Da unveränderliche Typen nicht geändert werden können, scheinen sie als Wert übergeben zu werden. Wenn Sie ein int an eine Funktion übergeben, wird das int dem Parameter der Funktion zugewiesen. Sie können das immer nur neu zuweisen, aber der ursprüngliche Variablenwert wird dadurch nicht geändert.

Daren Thomas
quelle
3
Auf den ersten Blick scheint diese Antwort die ursprüngliche Frage zu umgehen. Nach einer zweiten Lektüre wurde mir klar, dass dies die Sache ziemlich klar macht. Eine gute Fortsetzung
Christian Groleau
65

Effbot (auch bekannt als Fredrik Lundh) hat Pythons variablen Übergabestil als Call-by-Object beschrieben: http://effbot.org/zone/call-by-object.htm

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

  • Wenn Sie eine Zuweisung vornehmen, wie z. B. x = 1000, wird ein Wörterbucheintrag erstellt, der die Zeichenfolge "x" im aktuellen Namespace einem Zeiger auf das Ganzzahlobjekt zuordnet, das tausend enthält.

  • Wenn Sie "x" mit aktualisieren x = 2000, wird ein neues ganzzahliges Objekt erstellt und das Wörterbuch so aktualisiert, dass es auf das neue Objekt zeigt. Das alte tausend Objekt ist unverändert (und kann lebendig sein oder nicht, je nachdem, ob sich etwas anderes auf das Objekt bezieht).

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

  • Objekte wie Zeichenfolgen und Ganzzahlen sind unveränderlich . Dies bedeutet einfach, dass es keine Methoden gibt, mit denen das Objekt nach seiner Erstellung geändert werden kann. Wenn beispielsweise das ganzzahlige Objekt 1000 erstellt wurde, wird es sich nie mehr ändern. Für die Mathematik werden neue ganzzahlige Objekte erstellt.

  • Objekte wie Listen sind veränderbar . Dies bedeutet, dass der Inhalt des Objekts durch alles geändert werden kann, was auf das Objekt zeigt. Zum Beispiel x = []; y = x; x.append(10); print ywird gedruckt [10]. Die leere Liste wurde erstellt. Sowohl "x" als auch "y" zeigen auf dieselbe Liste. Das Append- Methode mutiert (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 (genau wie eine Datenbankaktualisierung für jede Verbindung zu dieser Datenbank sichtbar wäre).

Hoffe, das klärt das Problem für Sie.

Raymond Hettinger
quelle
2
Ich schätze es sehr, von einem Entwickler darüber zu lernen. Stimmt es, dass die id()Funktion den Wert des Zeigers (Objektreferenz) zurückgibt, wie die Antwort von pepr nahe legt?
Ehrliche Abe
4
@HonestAbe Ja, in CPython gibt id () die Adresse zurück. In anderen Pythons wie PyPy und Jython ist id () jedoch nur eine eindeutige Objektkennung.
Raymond Hettinger
59

Technisch, Python immer Pass-by-Referenzwerte . Ich werde meine andere Antwort wiederholen , um meine Aussage zu unterstützen.

Python verwendet immer Referenzwerte. Es gibt keine Ausnahme. Jede Variablenzuweisung bedeutet das Kopieren des Referenzwerts. Keine Ausnahmen. Jede Variable ist der Name, der an den Referenzwert gebunden ist. Immer.

Sie können sich einen Referenzwert als Adresse des Zielobjekts vorstellen. Die Adresse wird bei Verwendung automatisch dereferenziert. Auf diese Weise scheinen Sie beim Arbeiten mit dem Referenzwert direkt mit dem Zielobjekt zu arbeiten. Dazwischen gibt es immer eine Referenz, einen Schritt weiter, um zum Ziel zu springen.

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

Illustriertes Beispiel für die Übergabe des Arguments

Wenn das Argument als Wert übergeben wurde, wird das äußere lst nicht geändert werden. Das Grün sind die Zielobjekte (das Schwarz ist der darin gespeicherte Wert, das Rot ist der Objekttyp), das Gelb ist der Speicher mit dem Referenzwert im Inneren - gezeichnet als Pfeil. Der blaue durchgezogene 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 tatsächlich auch als grüne Ellipse gezeichnet werden. Die Farbe und die Form sagen nur, dass es intern ist.)

Du kannst den ... benutzen id() integrierten Funktion den Referenzwert (dh die Adresse des Zielobjekts) ermitteln.

In kompilierten Sprachen ist eine Variable ein Speicherplatz, der den Wert des Typs erfassen kann. In Python ist eine Variable ein Name (intern als Zeichenfolge erfasst), der an die Referenzvariable gebunden ist, die den Referenzwert für das Zielobjekt enthält. Der Name der Variablen ist der Schlüssel im internen Wörterbuch. Der Werteteil dieses Wörterbuchelements speichert den Referenzwert für das Ziel.

Referenzwerte sind in Python ausgeblendet. Es gibt keinen expliziten Benutzertyp zum Speichern des Referenzwerts. Sie können jedoch ein Listenelement (oder ein Element in einem anderen geeigneten Containertyp) als Referenzvariable verwenden, da alle Container die Elemente auch als Verweise auf die Zielobjekte speichern. Mit anderen Worten, Elemente sind tatsächlich nicht im Container enthalten - nur die Verweise auf Elemente.

pepr
quelle
1
Tatsächlich wird dies als Referenzwert bestätigt. +1 für diese Antwort, obwohl das Beispiel nicht gut war.
BugShotGG
30
Das Erfinden einer neuen Terminologie (z. B. "Referenzwert übergeben" oder "Objekt aufrufen" ist nicht hilfreich). "Call by (Wert | Referenz | Name)" sind Standardbegriffe. "Referenz" ist ein Standardbegriff. Das Übergeben von Referenzen nach Wert beschreibt das Verhalten von Python, Java und einer Vielzahl anderer Sprachen unter Verwendung der Standardterminologie genau.
Cayhorstmann
4
@cayhorstmann: Das Problem ist, dass die Python-Variable nicht dieselbe terminologische Bedeutung hat wie in anderen Sprachen. Auf diese Weise passt der Aufruf per Referenz hier nicht gut. Wie definieren Sie den Begriff Referenz genau ? Informell könnte der Python-Weg leicht als Übergabe der Adresse des Objekts beschrieben werden. Es passt jedoch nicht zu einer potenziell verteilten Implementierung von Python.
Pepr
1
Ich mag diese Antwort, aber Sie könnten überlegen, ob das Beispiel wirklich hilft oder den Fluss verletzt. Wenn Sie 'Referenzwert' durch 'Objektreferenz' ersetzen würden, würden Sie eine Terminologie verwenden, die wir als 'offiziell' betrachten könnten, wie hier zu sehen: Definieren von Funktionen
Honest Abe
2
Am Ende dieses Zitats befindet sich eine Fußnote mit der Aufschrift: "Tatsächlich wäre ein Aufruf per Objektreferenz eine bessere Beschreibung, da der Anrufer bei Übergabe eines veränderlichen Objekts alle Änderungen sieht, die der Angerufene daran vornimmt ... " Ich stimme Ihnen zu, dass Verwirrung dadurch entsteht, dass versucht wird, die mit anderen Sprachen festgelegte Terminologie anzupassen. Abgesehen von der Semantik müssen folgende Dinge verstanden werden: Wörterbücher / Namespaces, Namensbindungsoperationen und die Beziehung von Name → Zeiger → Objekt (wie Sie bereits wissen).
Ehrliche Abe
56

In Python gibt es keine Variablen

Der Schlüssel zum Verständnis der Parameterübergabe besteht darin, nicht mehr an "Variablen" zu denken. Es gibt Namen und Objekte in Python und zusammen erscheinen sie wie Variablen, aber es ist nützlich, die drei immer 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 dazu gehört. Die Veränderlichkeit spielt für diese Frage keine Rolle.

Beispiel:

a = 1

Dadurch wird der Name aan ein Objekt vom Typ Integer gebunden, das den Wert 1 enthält.

b = x

Dies bindet den Namen b an dasselbe Objekt xgebunden, an das der Name derzeit gebunden ist. Danach hat der Name bnichts mehr mit dem Namen zu tun x.

Siehe Abschnitte 3.1 und 4.2 in der Python 3-Sprachreferenz.

Wie lese ich das Beispiel in der Frage?

In dem in der Frage gezeigten Code self.Change(self.variable)bindet die Anweisung den Namen var(im Funktionsumfang Change) an das Objekt, das den Wert 'Original'und die Zuordnung enthältvar = 'Changed' (im Funktionskörper Change) weist denselben Namen erneut zu: einem anderen Objekt (das passiert) auch eine Saite zu halten, hätte aber etwas ganz anderes sein können).

Wie man als Referenz weitergibt

Wenn das zu ändernde Objekt also ein veränderliches Objekt ist, gibt es kein Problem, da alles effektiv als Referenz übergeben wird.

Wenn es sich um ein unveränderliches Objekt handelt (z. B. ein Bool, eine Zahl, eine Zeichenfolge), müssen Sie es in ein veränderliches Objekt einschließen.
Die schnelle und schmutzige Lösung hierfür ist eine Ein-Element-Liste (anstelle von self.variable, übergeben [self.variable]und in der Funktion ändern var[0]).
Der pythonischere Ansatz wäre die Einführung einer trivialen Klasse mit einem Attribut. Die Funktion empfängt eine Instanz der Klasse und bearbeitet das Attribut.

Lutz Prechelt
quelle
20
"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!
Ned Batchelder
9
Es mag schockierend sein, aber es ist nicht albern. Und ich denke auch nicht, dass es verwirrend ist: Es öffnet hoffentlich den Geist der Empfängerin für die bevorstehende Erklärung und versetzt sie in eine nützliche Haltung "Ich frage mich, was sie anstelle von Variablen haben". (Ja, Ihr Kilometerstand kann variieren.)
Lutz Prechelt
13
Würden Sie auch sagen, dass Javascript keine Variablen hat? Sie funktionieren genauso wie Pythons. Auch Java, Ruby, PHP, ... Ich denke, eine bessere Unterrichtstechnik ist: "Pythons Variablen funktionieren anders als die von C."
Ned Batchelder
9
Ja, Java hat Variablen. Python und JavaScript, Ruby, PHP usw. Sie würden in Java nicht sagen, dass inteine Variable deklariert wird, aber Integernicht. Beide deklarieren Variablen. Die IntegerVariable ist ein Objekt, die intVariable ist ein Grundelement. Als Beispiel haben Sie anhand der Darstellung gezeigt, wie Ihre Variablen funktionieren a = 1; b = a; a++ # doesn't modify b. Das gilt auch für Python (mit, += 1da es ++in Python keine gibt )!
Ned Batchelder
1
Das Konzept der "Variablen" ist komplex und oft vage: Eine Variable ist ein Container für einen Wert, der durch einen Namen identifiziert wird. In Python sind die Werte Objekte, die Container sind Objekte (siehe das Problem?) Und die Namen sind eigentlich separate Dinge. Ich glaube, es ist viel schwieriger, auf diese Weise ein genaues Verständnis der Variablen zu erlangen . Die Erklärung von Namen und Objekten erscheint schwieriger, ist aber tatsächlich einfacher.
Lutz Prechelt
43

Ein einfacher Trick, den ich normalerweise benutze, 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ß, dass dies unpraktisch sein kann, aber manchmal ist es einfach genug, dies zu tun.)

AmanicA
quelle
7
+1 für eine kleine Textmenge, die die wesentliche Problemumgehung für das Problem darstellt, dass Python keine Referenzübergabe hat. (Als Folgekommentar / Frage, die sowohl hier als auch irgendwo auf dieser Seite passt: 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 dies und behandeln Verweise auf das Argument innerhalb der Funktion als das 0. Element der Liste.)
M Katz
5
Nett. Um an ref vorbeizukommen, wickeln Sie [] ein.
Justas
38

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

Ich denke, es ist wichtig anzumerken, dass der aktuelle Beitrag mit den meisten Stimmen (von Blair Conrad), obwohl er in Bezug auf sein Ergebnis korrekt ist, irreführend ist und aufgrund seiner Definitionen grenzwertig falsch ist. Obwohl es viele Sprachen (wie C) gibt, in denen der Benutzer entweder als Referenz oder als Wert übergeben kann, gehört Python nicht dazu.

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

In dem Maße, in dem Python als Wert übergeben wird, werden alle Sprachen als Wert übergeben, da einige Daten (sei es ein "Wert" oder eine "Referenz") gesendet werden müssen. Dies bedeutet jedoch nicht, dass Python in dem Sinne als Wert übergeben wird, wie es ein C-Programmierer denken würde.

Wenn Sie das Verhalten wollen, ist Blair Conrads Antwort in Ordnung. Wenn Sie jedoch wissen möchten, warum Python weder als Wert noch als Referenz übergeben wird, lesen Sie die Antwort von David Cournapeau.

KobeJohn
quelle
4
Es ist einfach nicht wahr, dass alle Sprachen nach Wert aufgerufen werden. In C ++ oder Pascal (und sicherlich vielen anderen, die ich nicht kenne) haben Sie einen Aufruf als Referenz. In C ++ void swap(int& x, int& y) { int temp = x; x = y; y = temp; }werden beispielsweise die übergebenen Variablen ausgetauscht. In Pascal verwenden Sie varanstelle von &.
Cayhorstmann
2
Ich dachte, ich hätte vor langer Zeit darauf geantwortet, aber ich sehe es nicht. Der Vollständigkeit halber hat Cayhorstmann meine Antwort falsch verstanden. Ich habe nicht gesagt, dass alles nach Wert benannt ist, wie es die meisten Leute zuerst in Bezug auf C / C ++ lernen . Es war einfach so, dass ein Wert übergeben wurde (Wert, Name, Zeiger usw.) und dass die in Blairs ursprünglicher Antwort verwendeten Begriffe ungenau waren.
KobeJohn
24

Sie haben hier einige wirklich gute Antworten.

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )
Bobobobo
quelle
2
Ja, wenn Sie jedoch x = [2, 4, 4, 5, 5], y = x, X [0] = 1 ausführen, drucken Sie x # [1, 4, 4, 5, 5] und drucken Sie y # [1 , 4, 4, 5, 5]
Laycat
19

In diesem Fall wird der varin der Methode betitelten Variablen Changeein Verweis zugewiesen self.variable, und Sie weisen sofort eine Zeichenfolge zu var. Es zeigt nicht mehr auf self.variable. Das folgende Codefragment zeigt, was passieren würde, wenn Sie die Datenstruktur ändern, auf die durch varund self.variablein diesem Fall eine Liste verwiesen wird :

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

Ich bin sicher, jemand anderes könnte dies weiter klären.

Mike Mazur
quelle
18

Das Pass-by-Assignment-Schema von Python ist nicht ganz das gleiche wie die Referenzparameteroption von C ++, aber es stellt sich heraus, dass es dem Argument-Passing-Modell der C-Sprache (und anderer) in der Praxis sehr ähnlich ist:

  • Unveränderliche Argumente werden effektiv " nach Wert " übergeben. Objekte wie Ganzzahlen und Zeichenfolgen werden nicht durch Kopieren, sondern durch Objektreferenz übergeben. Da Sie jedoch unveränderliche Objekte ohnehin nicht ändern können, ähnelt der Effekt dem Erstellen einer Kopie.
  • Veränderbare Argumente werden effektiv " per Zeiger " übergeben. Objekte wie Listen und Wörterbücher werden ebenfalls als Objektreferenz übergeben. Dies ähnelt der Art und Weise, wie C Arrays als Zeiger übergibt. Veränderbare Objekte können in der Funktion geändert werden, ähnlich wie C-Arrays.
ajknzhol
quelle
16

Wie Sie sagen können, benötigen Sie ein veränderbares Objekt, aber ich möchte Ihnen vorschlagen, die globalen Variablen zu überprüfen, da sie Ihnen helfen oder sogar diese Art von Problem lösen können!

http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

Beispiel:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2
Nuno Aniceto
quelle
5
Ich war versucht, eine ähnliche Antwort zu veröffentlichen - der ursprüngliche Fragesteller wusste möglicherweise nicht, dass er tatsächlich eine globale Variable verwenden wollte, die von Funktionen gemeinsam genutzt wird. Hier ist der Link, den ich geteilt hätte: stackoverflow.com/questions/423379/… Als Antwort auf @Tim ist Stack Overflow nicht nur eine Frage- und Antwortseite, sondern ein riesiges Repository an Referenzwissen, das nur noch stärker und nuancierter wird wie ein aktives Wiki mit mehr Input.
Max P Magee
13

Viele Einblicke in die Antworten hier, aber ich denke, ein zusätzlicher Punkt wird hier nicht explizit erwähnt. Zitieren aus der Python-Dokumentation https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

"In Python sind Variablen, auf die nur innerhalb einer Funktion verwiesen wird, implizit global. Wenn einer Variablen irgendwo im Funktionskörper ein neuer Wert zugewiesen wird, wird davon ausgegangen, dass es sich um einen lokalen Wert handelt. Wenn einer Variablen jemals ein neuer Wert innerhalb der Funktion zugewiesen wird, Die Variable ist implizit lokal, und Sie müssen sie explizit als "global" deklarieren. Obwohl dies zunächst etwas überraschend ist, erklärt eine kurze Überlegung dies. Einerseits bietet das Erfordernis "global" für zugewiesene Variablen einen Balken gegen unbeabsichtigte Nebenwirkungen Wenn andererseits global für alle globalen Referenzen erforderlich wäre, würden Sie immer global verwenden. Sie müssten jede Referenz auf eine integrierte Funktion oder auf eine Komponente eines importierten Moduls als global deklarieren. Diese Unordnung würde die Nützlichkeit der globalen Erklärung zur Identifizierung von Nebenwirkungen zunichte machen. "

Dies gilt auch dann, wenn ein veränderliches Objekt an eine Funktion übergeben wird. Und mir erklärt klar den Grund für den Unterschied im Verhalten zwischen der Zuordnung zum Objekt und der Bearbeitung des Objekts in der Funktion.

def test(l):
    print "Received", l , id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

gibt:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

Die Zuweisung zu einer globalen Variablen, die nicht als global deklariert ist, erstellt daher ein neues lokales Objekt und unterbricht die Verknüpfung zum ursprünglichen Objekt.

Joop
quelle
10

Hier ist die einfache (ich hoffe) Erklärung des pass by objectin Python verwendeten Konzepts .
Immer wenn Sie ein Objekt an die Funktion übergeben, wird das Objekt selbst übergeben (Objekt in Python ist tatsächlich das, was Sie in anderen Programmiersprachen als Wert bezeichnen würden), nicht der Verweis auf dieses Objekt. Mit anderen Worten, wenn Sie anrufen:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

Das eigentliche Objekt - [0, 1] (das in anderen Programmiersprachen als Wert bezeichnet wird) wird übergeben. Tatsächlich wird die Funktion also change_meversuchen, Folgendes zu tun:

[0, 1] = [1, 2, 3]

Dadurch wird das an die Funktion übergebene Objekt offensichtlich nicht geändert. Wenn die Funktion so aussah:

def change_me(list):
   list.append(2)

Dann würde der Anruf ergeben:

[0, 1].append(2)

was offensichtlich das Objekt ändern wird. Diese Antwort erklärt es gut.

Matino
quelle
2
Das Problem ist, dass die Zuweisung etwas anderes tut, als Sie erwarten. Die list = [1, 2, 3]Ursachen , die die Wiederverwendung von listNamen für etwas anderes und forgeting das ursprünglich übergebene Objekt. Sie können es jedoch versuchen list[:] = [1, 2, 3](übrigens listist der falsche Name für eine Variable. Nachdenken [0, 1] = [1, 2, 3]ist ein völliger Unsinn. Was bedeutet Ihrer Meinung nach , dass das Objekt selbst übergeben wird ? Was wird Ihrer Meinung nach in die Funktion kopiert?
pepr
@ pepr-Objekte sind keine Literale. Sie sind Objekte. Die einzige Möglichkeit, über sie zu sprechen, besteht darin, ihnen einige Namen zu geben. Deshalb ist es so einfach, wenn man es erst einmal verstanden hat, aber es ist enorm kompliziert zu erklären. :-)
Veky
@ Veky: Das ist mir bewusst. Auf jeden Fall wird das Listenliteral in das Listenobjekt konvertiert. Tatsächlich kann jedes Objekt in Python ohne Namen existieren und auch dann verwendet werden, wenn kein Name angegeben wird. Und Sie können an sie wie an anonyme Objekte denken. Stellen Sie sich Objekte als Elemente einer Liste vor. Sie brauchen keinen Namen. Sie können auf sie zugreifen, indem Sie die Liste indizieren oder durchlaufen. Wie auch immer, ich bestehe darauf, [0, 1] = [1, 2, 3]ist einfach ein schlechtes Beispiel. In Python gibt es nichts Vergleichbares.
Pepr
@pepr: Ich meine nicht unbedingt Python-Definitionsnamen, sondern nur gewöhnliche Namen. Natürlich alist[2]zählt als Name eines dritten Elistenelements. Aber ich glaube, ich habe falsch verstanden, was Ihr Problem war. :-)
Veky
Argh. Mein Englisch ist offensichtlich viel schlechter als mein Python. :-) Ich werde es noch einmal versuchen. Ich habe nur gesagt, dass Sie dem Objekt einige Namen geben müssen, um darüber zu sprechen. Mit diesen "Namen" meinte ich nicht "Namen wie von Python definiert". Ich kenne Python-Mechanismen, keine Sorge.
Veky
8

Abgesehen von all den tollen Erklärungen, wie dieses Zeug in Python funktioniert, sehe ich keinen einfachen Vorschlag für das Problem. Wenn Sie scheinbar Objekte und Instanzen erstellen, können Sie Instanzvariablen auf folgende Weise pythonisch verarbeiten und ändern:

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

    def Change(self):
        self.variable = 'Changed'

In Instanzmethoden beziehen Sie sich normalerweise auf selfden Zugriff auf Instanzattribute. Es ist normal, Instanzattribute in Instanzmethoden festzulegen __init__und diese zu lesen oder zu ändern. Deshalb geben Sie auch selfals erstes Argument an def Change.

Eine andere Lösung wäre, eine statische Methode wie diese zu erstellen:

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

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var
Dolf Andringa
quelle
6

Es gibt einen kleinen Trick, um ein Objekt als Referenz zu übergeben, obwohl die Sprache dies nicht ermöglicht. Es funktioniert auch in Java, es ist die Liste mit einem Element. ;-);

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print obj.name

p = [obj] # A pointer to obj! ;-)
changeRef(p)

print p[0].name # p->name

Es ist ein hässlicher Hack, aber es funktioniert. ;-P

itmuckel
quelle
pist eine Referenz auf ein veränderbares Listenobjekt, in dem das Objekt gespeichert ist obj. Die Referenz 'p' wird übergeben changeRef. Im Inneren changeRefwird eine neue Referenz erstellt (die neue Referenz wird aufgerufen ref), die auf dasselbe Listenobjekt verweist, auf das verwiesen wird p. Da Listen jedoch veränderbar sind, sind Änderungen an der Liste für beide Referenzen sichtbar . In diesem Fall haben Sie die refReferenz verwendet, um das Objekt am Index 0 so zu ändern, dass es anschließend das PassByReference('Michael')Objekt speichert . Die Änderung am Listenobjekt wurde mit vorgenommen ref, diese Änderung ist jedoch für sichtbar p.
Minh Tran
Die Verweise pund refverweisen nun auf ein Listenobjekt, in dem das einzelne Objekt gespeichert ist PassByReference('Michael'). Daraus folgt, dass p[0].namezurückkehrt Michael. Natürlich refist jetzt aus dem Rahmen gegangen und kann Müll gesammelt werden, aber trotzdem.
Minh Tran
Sie haben jedoch die private Instanzvariable namedes ursprünglichen PassByReferenceObjekts, das der Referenz zugeordnet ist obj, nicht geändert . In der Tat obj.namewird zurückkehren Peter. Die oben genannten Kommentare setzen die Definition voraus Mark Ransom.
Minh Tran
Ich stimme nicht zu, dass es sich um einen Hack handelt (was ich unter etwas verstehe, das funktioniert, aber aus unbekannten, nicht getesteten oder vom Implementierer nicht beabsichtigten Gründen). Sie haben einfach ein PassByReferenceObjekt durch ein anderes PassByReferenceObjekt in Ihrer Liste ersetzt und auf das letztere der beiden Objekte verwiesen.
Minh Tran
5

Ich habe die folgende Methode verwendet, um schnell ein paar Fortran-Codes in Python zu konvertieren. Es ist wahr, dass es nicht als Referenz dient, da die ursprüngliche Frage gestellt wurde, sondern in einigen Fällen eine einfache Lösung ist.

a=0
b=0
c=0
def myfunc(a,b,c):
    a=1
    b=2
    c=3
    return a,b,c

a,b,c = myfunc(a,b,c)
print a,b,c
Brad Porter
quelle
Ja, dies löst auch in meinem Anwendungsfall das "Pass by Reference". Ich habe eine Funktion, die im Grunde genommen Werte in a bereinigt dictund dann die zurückgibt dict. Während der Reinigung kann sich jedoch herausstellen, dass ein Teil des Systems neu aufgebaut werden muss. Daher muss die Funktion nicht nur das Gereinigte zurückgeben, dictsondern auch den Wiederaufbau signalisieren können. Ich habe versucht, eine boolReferenz zu übergeben, aber das funktioniert natürlich nicht. Als ich herausfand, wie ich das lösen kann, fand ich, dass Ihre Lösung (im Grunde genommen ein Tupel zurück) am besten funktioniert und gleichzeitig überhaupt kein Hack / Workaround ist (IMHO).
Kasimir
3

Während das Übergeben von Referenzen nichts ist, was gut in Python passt und selten verwendet werden sollte, gibt es einige Problemumgehungen, mit denen das aktuell einer lokalen Variablen zugewiesene Objekt abgerufen oder sogar eine lokale Variable innerhalb einer aufgerufenen Funktion neu zugewiesen werden kann.

Die Grundidee besteht darin, eine Funktion zu haben, die diesen Zugriff ermöglicht und als Objekt an andere Funktionen übergeben oder in einer Klasse gespeichert werden kann.

Eine Möglichkeit ist die Verwendung global(für globale Variablen) odernonlocal (für lokale Variablen in einer Funktion) in einer Wrapper-Funktion zu verwenden.

def change(wrapper):
    wrapper(7)

x = 5
def setter(val):
    global x
    x = val
print(x)

Die gleiche Idee funktioniert beim Lesen und del Ätzen einer Variablen.

Zum Lesen gibt es sogar eine kürzere Möglichkeit, nur zu verwenden lambda: x der ein Callable zurückgegeben wird, der beim Aufruf den aktuellen Wert von x zurückgibt. Dies ist etwas wie "Call by Name", das in Sprachen in der fernen Vergangenheit verwendet wurde.

Das Übergeben von 3 Wrappern für den Zugriff auf eine Variable ist etwas unhandlich, sodass diese in eine Klasse mit einem Proxy-Attribut eingeschlossen werden können:

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

Die Pythons "Reflection" -Unterstützung ermöglicht es, ein Objekt zu erhalten, das einen Namen / eine Variable in einem bestimmten Bereich neu zuweisen kann, ohne Funktionen explizit in diesem Bereich zu definieren:

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

Hier ByRefschließt die Klasse einen Wörterbuchzugriff ein. Der Attributzugriff auf wrappedwird also in einen Elementzugriff im übergebenen Wörterbuch übersetzt. Durch Übergeben des Ergebnisses des eingebauten localsund des Namens einer lokalen Variablen wird auf eine lokale Variable zugegriffen . Die Python-Dokumentation ab 3.5 weist darauf hin, dass das Ändern des Wörterbuchs möglicherweise nicht funktioniert, aber für mich zu funktionieren scheint.

Textshell
quelle
3

Angesichts der Art und Weise, wie Python mit Werten umgeht und auf diese verweist, können Sie ein beliebiges Instanzattribut nur nach Namen referenzieren:

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

    def change(self, var):
        self.__dict__[var] = 'Changed'

In echtem Code würden Sie natürlich eine Fehlerprüfung bei der Diktatsuche hinzufügen.

mARK bLOORE
quelle
3

Da Wörterbücher als Referenz übergeben werden, können Sie eine dict-Variable verwenden, um darin referenzierte Werte zu speichern.

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!" # reference any string (errors, e.t.c). ref['msg'] is string
    return result # return the sum

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the return value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value
Liakos
quelle
3

Pass-By-Reference in Python unterscheidet sich erheblich vom Konzept der Pass-By-Reference in C ++ / Java.

  • Java & C #: Primitive Typen (einschließlich Zeichenfolge) werden als Wert übergeben (Kopie), Referenztyp wird als Referenz übergeben (Adresskopie), sodass alle Änderungen, die am Parameter in der aufgerufenen Funktion vorgenommen wurden, für den Aufrufer sichtbar sind.
  • C ++: Sowohl Referenzübergabe als auch Wertübergabe sind zulässig. Wenn ein Parameter als Referenz übergeben wird, können Sie ihn entweder ändern oder nicht, je nachdem, ob der Parameter als const übergeben wurde oder nicht. Ob const oder nicht, der Parameter behält jedoch die Referenz auf das Objekt bei, und die Referenz kann nicht zugewiesen werden, um auf ein anderes Objekt innerhalb der aufgerufenen Funktion zu verweisen.
  • Python: Python ist eine "Pass-by-Object-Referenz", von der oft gesagt wird: "Objektreferenzen werden als Wert übergeben." [Hier lesen] 1. Sowohl der Aufrufer als auch die Funktion beziehen sich auf dasselbe Objekt, aber der Parameter in der Funktion ist eine neue Variable, die nur eine Kopie des Objekts im Aufrufer enthält. Wie in C ++ kann ein Parameter entweder geändert werden oder nicht in der Funktion - Dies hängt vom Typ des übergebenen Objekts ab. z.B; Ein unveränderlicher Objekttyp kann in der aufgerufenen Funktion nicht geändert werden, während ein veränderliches Objekt entweder aktualisiert oder neu initialisiert werden kann. Ein entscheidender Unterschied zwischen dem Aktualisieren oder Neuzuweisen / Neuinitialisieren der veränderlichen Variablen besteht darin, dass der aktualisierte Wert in der aufgerufenen Funktion wiedergegeben wird, während der neu initialisierte Wert dies nicht tut. Der Umfang einer Zuweisung eines neuen Objekts zu einer veränderlichen Variablen ist lokal für die Funktion in der Python. Beispiele von @ blair-conrad sind großartig, um dies zu verstehen.
Alok Garg
quelle
1
Alt, aber ich fühle mich verpflichtet, es zu korrigieren. Zeichenfolgen werden sowohl in Java als auch in C # als Referenz übergeben, NICHT als Wert
John
2

Da Ihr Beispiel zufällig objektorientiert ist, können Sie die folgenden Änderungen vornehmen, um ein ähnliches Ergebnis zu erzielen:

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

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

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'
Jesse Hogan
quelle
1
Obwohl das funktioniert. Es wird nicht als Referenz übergeben. Es ist "Objektreferenz übergeben".
Bishwas Mishra
1

Sie können lediglich eine leere Klasse als Instanz zum Speichern von Referenzobjekten verwenden, da intern Objektattribute in einem Instanzwörterbuch gespeichert werden. Siehe das Beispiel.

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)
Sergzach
quelle
1

Da es nirgends erwähnt zu werden scheint, besteht ein Ansatz zur Simulation von Referenzen, wie er beispielsweise aus C ++ bekannt ist, darin, eine "Update" -Funktion zu verwenden und diese anstelle der tatsächlichen Variablen (oder besser "Name") zu übergeben:

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

Dies ist meistens nützlich für "Nur-Out-Referenzen" oder in einer Situation mit mehreren Threads / Prozessen (indem die Aktualisierungsfunktion Thread / Multiprocessing sicher gemacht wird).

Offensichtlich erlaubt das Obige nicht, den Wert zu lesen , sondern nur zu aktualisieren.

Daniel Jour
quelle