it-swarm.com.de

Wie übergebe ich eine Variable als Referenz?

Die Dokumentation zu Python scheint unklar zu sein, ob Parameter als Referenz oder als 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?

2393
David Sykes

Argumente sind durch Zuweisung übergeben . Dies hat zwei Gründe:

  1. der übergebene Parameter ist eigentlich eine Referenz auf ein Objekt (aber die Referenz wird 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 ihn nach Herzenslust ändern, aber wenn Sie den Verweis in der Methode erneut binden 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 Sie können das Objekt nicht einmal mutieren.

Um es noch deutlicher zu machen, wollen wir 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 eine Referenz auf outer_list und keine Kopie davon ist, können wir die Methoden der mutierenden Liste verwenden, um sie zu ändern und die Änderungen im äußeren Gültigkeitsbereich widerzuspiegeln.

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 Parameter the_list als Wert übergeben wurde, hatte das Zuweisen einer neuen Liste keine Auswirkungen, die der Code außerhalb der Methode sehen konnte. Der the_list war eine Kopie der outer_list Referenz, und wir hatten the_list auf eine neue Liste zeigen lassen, aber es gab keine Möglichkeit, zu ändern, wohin outer_list zeigte.

String - ein unveränderlicher Typ

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

Nun wollen wir versuchen, 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 Parameter the_string als Wert übergeben wurde, hatte die Zuweisung einer neuen Zeichenfolge keine Auswirkung, die der Code außerhalb der Methode sehen konnte. Der the_string war eine Kopie der outer_string Referenz, und wir hatten the_string auf eine neue Zeichenkette zeigen lassen, aber es gab keine Möglichkeit zu ändern, wo outer_string zeigte.

Ich hoffe, das klärt ein bisschen auf.

EDIT: Es wurde bemerkt, dass dies nicht die Frage beantwortet, die @David ursprünglich gestellt hatte: "Gibt es etwas, was ich tun kann, um die Variable als Ist zu übergeben? Referenz?". Lass uns daran arbeiten.

Wie können wir das umgehen?

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

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.

2585
Blair Conrad

Das Problem beruht auf 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 geschieht:

a = 1
a = 2

Sie glauben, dass a ein Speicherort ist, der den Wert 1 speichert und dann aktualisiert wird, um den Wert 2 zu speichern. So funktioniert das in Python nicht. Vielmehr beginnt a als Referenz auf ein Objekt mit dem Wert 1 und wird dann als Referenz auf ein Objekt mit dem Wert 2 neu zugewiesen. Diese beiden Objekte können weiterhin nebeneinander existieren, obwohl a nicht mehr auf das erste verweist. 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 sich auf das übergebene Objekt bezieht. Diese Referenz ist nicht mit der Referenz identisch, die im Funktionsaufruf verwendet wurde. Daher besteht keine Möglichkeit, diese Referenz zu aktualisieren und auf a zu verweisen neues Objekt. In deinem Beispiel:

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

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

self.variable ist eine Referenz auf das String-Objekt 'Original'. Wenn Sie Change aufrufen, erstellen Sie einen zweiten Verweis var auf das Objekt. Innerhalb der Funktion weisen Sie die Referenz var einem anderen Zeichenfolgenobjekt 'Changed' zu, die Referenz self.variable ist jedoch getrennt 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'
627
Mark Ransom

Ich fand die anderen Antworten ziemlich lang und kompliziert, daher habe ich dieses einfache Diagramm erstellt, um zu erklären, wie Python mit Variablen und Parametern umgeht. enter image description here

296
Zenadix

Es ist weder eine Wertübergabe noch eine Referenzübergabe - es ist eine Objektübergabe. Sehen Sie 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."

Wenn in Ihrem Beispiel die Change -Methode aufgerufen wird, wird ein Namespace dafür erstellt. und var wird in diesem Namespace zu einem Namen für das Zeichenfolgenobjekt _'Original'_. Dieses Objekt hat dann einen Namen in zwei Namespaces. Als Nächstes bindet _var = 'Changed'_ var an ein neues Zeichenfolgenobjekt, und der Namespace der Methode vergisst daher _'Original'_. Schließlich wird dieser Namespace vergessen und die Zeichenfolge _'Changed'_ mit.

231

Denken Sie an Dinge, die übergeben werden durch Zuweisung statt durch Referenz/durch Wert. Auf diese Weise ist immer klar, was passiert, solange Sie verstehen, was während des normalen Einsatzes 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. Wenn Sie die Liste innerhalb neu zuweisen, 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 in eine Funktion übergeben, weisen Sie das int dem Funktionsparameter zu. Sie können das immer nur neu zuweisen, aber der Wert der ursprünglichen Variablen wird nicht geändert.

159
Daren Thomas

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

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

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

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

  • Wenn Sie eine neue Zuweisung vornehmen, 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 lediglich, dass es keine Methoden gibt, mit denen das Objekt nach seiner Erstellung geändert werden kann. Wenn beispielsweise ein ganzzahliges Objekt mit dem Wert 1000 erstellt wurde, ändert es sich nie mehr. Die Berechnung erfolgt durch Erstellen neuer ganzzahliger Objekte.

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

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

62

Technisch gesehen verwendet Python immer Referenzwerte zum Übergeben . Ich wiederhole meine andere Antwort , um meine Aussage zu stützen.

Python verwendet immer Pass-by-Reference-Werte. Es gibt keine Ausnahme. Bei einer Variablenzuordnung wird der Referenzwert kopiert. 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 scheint es, als würden Sie mit dem Referenzwert direkt mit dem Zielobjekt arbeiten. Dazwischen befindet sich jedoch immer eine Referenz, um einen Schritt weiter zum Ziel zu springen.

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

Illustrated example of passing the argument

Wenn das Argument als Wert übergeben wurde, konnte das äußere lst nicht geändert werden. Das Grün ist das Zielobjekt (das Schwarz ist der darin gespeicherte Wert, das Rot ist der Objekttyp), das Gelb ist der Speicher mit dem als Pfeil eingezeichneten Referenzwert. Der blaue durchgezogene Pfeil ist der Referenzwert, der an die Funktion übergeben wurde (über den gestrichelten Pfad mit dem blauen Pfeil). Das hässliche dunkle Gelb ist das interne Wörterbuch. (Es könnte tatsächlich auch als grüne Ellipse gezeichnet werden. Die Farbe und die Form besagen nur, dass es sich um eine interne handelt.)

Mit der integrierten Funktion id() können Sie den Referenzwert (dh die Adresse des Zielobjekts) ermitteln.

In kompilierten Sprachen ist eine Variable ein Speicherbereich, 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 im Ziel.

Referenzwerte werden 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 Referenzen auf die Zielobjekte speichern. Mit anderen Worten, Elemente sind tatsächlich nicht im Container enthalten, sondern nur die Verweise auf Elemente.

59
pepr

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

42
AmanicA

In Python gibt es keine Variablen

Der Schlüssel zum Verständnis der Parameterübergabe liegt 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. Wenn Sie ein Argument an eine Funktion übergeben, wird auch ein Name (der Parametername der Funktion) an ein Objekt gebunden.

Das ist alles was dazu gehört. Die Veränderbarkeit ist für diese Frage unerheblich.

Beispiel:

a = 1

Dadurch wird der Name a an ein Objekt vom Typ Ganzzahl gebunden, das den Wert 1 enthält.

b = x

Dadurch wird der Name b an dasselbe Objekt gebunden, an das der Name x derzeit gebunden ist. Danach hat der Name b nichts mehr mit dem Namen x zu tun.

Siehe Abschnitte .1 und 4.2 in der Sprachreferenz Python 3.

Wie liest man das Beispiel in der Frage

In dem in der Frage gezeigten Code bindet die Anweisung self.Change(self.variable) den Namen var (im Funktionsumfang Change) an das Objekt, das den Wert 'Original' und die Zuweisung enthält var = 'Changed' (im Hauptteil der Funktion Change) weist diesen Namen erneut zu: einem anderen Objekt (das zufällig auch eine Zeichenfolge enthält, aber auch ganz anders hätte sein können).

Wie man als Referenz weitergibt

(edit 2019-04-28)

Wenn es sich also bei dem zu ändernden Objekt um ein veränderliches Objekt handelt, ist dies kein Problem, da alles effektiv als Referenz übergeben wird.

Wenn es sich um ein nveränderliches Objekt handelt (z. B. ein Bool, eine Zahl, eine Zeichenfolge), müssen Sie es in ein veränderliches Objekt einwickeln.
Die schnelle und schmutzige Lösung hierfür ist eine Liste mit einem Element (anstelle von self.variable[self.variable] übergeben und in der Funktion var[0] ändern).
Der mehr Pythonic Ansatz würde darin bestehen, eine triviale Klasse mit einem Attribut einzuführen. Die Funktion empfängt eine Instanz der Klasse und bearbeitet das Attribut.

40
Lutz Prechelt

(edit - Blair hat seine enorm beliebte Antwort aktualisiert, so dass sie jetzt korrekt ist.)

Ich denke, es ist wichtig anzumerken, dass der derzeitige Beitrag mit den meisten Stimmen (von Blair Conrad) zwar in Bezug auf sein Ergebnis korrekt ist, aber irreführend und aufgrund seiner Definition grenzwertig falsch ist. Zwar gibt es viele Sprachen (wie C), die es dem Benutzer ermöglichen, entweder als Referenz oder als Wert zu übergeben, aber Python ist keine davon.

Die Antwort von David Cournapeau verweist auf die eigentliche Antwort und erklärt, warum das Verhalten in Blair Conrads Beitrag korrekt zu sein scheint, obwohl die Definitionen nicht stimmen.

Soweit 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 als Wert in dem Sinne übergeben wird, wie ein C-Programmierer es sich vorstellen würde.

Wenn Sie das Verhalten wollen, ist die Antwort von Blair Conrad 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.

37
KobeJohn

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' )
23
bobobobo

Das Python-Pass-by-Assignment-Schema ist nicht ganz das gleiche wie die Referenzparameter-Option in C++, es ähnelt jedoch dem Argument-Passing-Modell der C-Sprache (und anderer) in der Praxis:

  • Unveränderliche Argumente werden effektiv mit "nach Wert" übergeben. Objekte wie Ganzzahlen und Zeichenfolgen werden als Objektreferenz übergeben, anstatt durch Kopieren. Da Sie jedoch unveränderliche Objekte nicht an der richtigen Stelle ändern können, ist der Effekt erheblich wie eine Kopie machen.
  • Veränderbare Argumente werden effektiv "per Zeiger" übergeben. Objekte wie Listen und Wörterbücher werden ebenfalls per Objektreferenz übergeben, ähnlich wie C Arrays als Zeiger übergibt. Veränderbare Objekte können an der richtigen Stelle in geändert werden die Funktion, ähnlich wie C-Arrays.
17
ajknzhol

In diesem Fall wird der Variablen var in der Methode Change eine Referenz auf self.variable zugewiesen, und Sie weisen var sofort eine Zeichenfolge zu. Es zeigt nicht mehr auf self.variable. Der folgende Codeausschnitt zeigt, was passieren würde, wenn Sie die Datenstruktur ändern, auf die var und self.variable zeigen, in diesem Fall eine Liste:

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

17
Mike Mazur

Wie Sie sagen können, müssen Sie ein veränderbares Objekt haben, aber lassen Sie mich 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
15
Nuno Aniceto

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 sie lokal ist. 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. Auch wenn dies zunächst etwas überraschend ist, wird dies durch eine kurze Überlegung erklärt. Einerseits bietet die Forderung nach "global" für zugewiesene Variablen einen Schutz vor unbeabsichtigten Nebenwirkungen Wenn andererseits global für alle globalen Verweise erforderlich wäre, würden Sie immer global verwenden. Sie müssten jeden Verweis auf eine integrierte Funktion oder eine Komponente eines importierten Moduls als global deklarieren würde den Nutzen der globalen Erklärung für die 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 das unterschiedliche Verhalten zwischen dem Zuweisen zum Objekt und dem Bedienen 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.

13
Joop

Hier ist die einfache (ich hoffe) Erklärung des in Python verwendeten Konzepts pass by object.
Immer wenn Sie ein Objekt an die Funktion übergeben, wird das Objekt selbst übergeben (Objekt in Python ist eigentlich 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] (in anderen Programmiersprachen als Wert bezeichnet) wird übergeben. Tatsächlich versucht die Funktion change_me so etwas wie:

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

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

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

Dann würde der Anruf ergeben:

[0, 1].append(2)

das wird natürlich das Objekt ändern. Diese Antwort erklärt es gut.

10
matino

Abgesehen von all den tollen Erklärungen, wie dieses Zeug in Python funktioniert, sehe ich keinen einfachen Vorschlag für das Problem. Während Sie Objekte und Instanzen zu erstellen scheinen, ist die pythonische Art, Instanzvariablen zu behandeln und zu ändern, die folgende:

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 self, um auf Instanzattribute zuzugreifen. Es ist normal, Instanzattribute in __init__ festzulegen und diese in Instanzmethoden zu lesen oder zu ändern. Das ist auch der Grund, warum Sie self als erstes Argument an def Change übergeben.

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
8
Dolf Andringa

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

6
itmuckel

Ich habe die folgende Methode verwendet, um schnell einige Fortran-Codes in Python zu konvertieren. Es ist wahr, es ist keine Referenz, wie die ursprüngliche Frage gestellt wurde, aber in einigen Fällen eine einfache Abhilfe.

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
4
Brad Porter

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

  • Java & C #: Primitivtypen (einschließlich Zeichenfolge) werden als Wert (Kopie) übergeben. Der Referenztyp wird als Referenz (Adresskopie) übergeben, sodass alle Änderungen in der Parameter in der aufgerufenen Funktion sind für den Aufrufer sichtbar.
  • 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 den Verweis auf das Objekt bei, und der Verweis 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 übergeben nach Wert. ”[Lesen Sie hier] 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 in der Funktion entweder geändert oder nicht geändert werden. Dies hängt vom Typ des übergebenen Objekts ab. z.B; Ein unveränderlicher Objekttyp kann in der aufgerufenen Funktion nicht geändert werden, wohingegen ein veränderliches Objekt entweder aktualisiert oder neu initialisiert werden kann. Ein entscheidender Unterschied zwischen der Aktualisierung oder Neuzuweisung/Neuinitialisierung der veränderlichen Variablen besteht darin, dass der aktualisierte Wert in der aufgerufenen Funktion wiedergegeben wird, der neu initialisierte Wert jedoch nicht. Der Umfang einer Zuweisung eines neuen Objekts zu einer veränderlichen Variablen ist lokal für die Funktion in der Python. Beispiele, die von @ blair-conrad bereitgestellt werden, sind großartig, um dies zu verstehen.
3
Alok Garg

in Anbetracht der Art und Weise, wie python mit Werten und Verweisen auf sie umgeht, können Sie ein beliebiges Instanzattribut nur über den 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.

3
mARK bLOORE

Während Referenzübergabe nichts ist, das gut in python passt und selten verwendet werden sollte, gibt es einige Problemumgehungen, mit denen das aktuell einer lokalen Variablen zugewiesene Objekt tatsächlich abgerufen oder sogar eine lokale Variable innerhalb von a neu zugewiesen werden kann Funktion aufgerufen.

Die Grundidee ist, 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 besteht darin, global (für globale Variablen) oder nonlocal (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 gilt für das Lesen und del Löschen einer Variablen.

Zum einfachen Lesen gibt es sogar einen kürzeren Weg, einfach lambda: x zu verwenden, der einen aufrufbaren Wert zurückgibt, der beim Aufrufen den aktuellen Wert von x zurückgibt. Dies ähnelt in gewisser Weise der Bezeichnung "Call by Name", die in Sprachen in der fernen Vergangenheit verwendet wurde.

Das Übergeben von drei Wrappern für den Zugriff auf eine Variable ist etwas umständlich, 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 "Reflection" -Unterstützung von Pythons ermöglicht es, ein Objekt zu erhalten, das einen Namen/eine Variable in einem bestimmten Bereich neu zuweisen kann, ohne Funktionen in diesem Bereich explizit 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 schließt die Klasse ByRef einen Wörterbuchzugriff ab. So wird der Attributzugriff auf wrapped in einen Elementzugriff im übergebenen Wörterbuch übersetzt. Durch Übergabe des Ergebnisses des eingebauten locals und 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 anscheinend funktioniert.

3
textshell

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'
2
Jesse Hogan

Da es anscheinend nirgends erwähnt wird, ist ein Ansatz zur Simulation von Referenzen bekannt, wie er z.B. C++ verwendet eine "update" -Funktion und übergibt diese anstelle der eigentlichen Variablen (oder besser "name"):

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 vor allem für "Nur-aus-Verweise" oder in Situationen mit mehreren Threads/Prozessen nützlich (indem die Aktualisierungsfunktion Thread/Multiprocessing sicher gemacht wird).

Offensichtlich erlaubt das Obige nicht Lesen den Wert, nur das Aktualisieren.

1
Daniel Jour

Da Wörterbücher als Referenz übergeben werden, können Sie eine dict-Variable verwenden, um referenzierte Werte darin 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
0
Liakos

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