übergeben - python variable aus anderer funktion




Wie kann ich eine Variable als Referenz übergeben? (16)

Es gibt keine Variablen in Python

Der Schlüssel zum Verständnis der Parameterübergabe besteht darin, nicht länger über "Variablen" nachzudenken. Es gibt Namen und Objekte in Python und zusammen erscheinen sie als Variablen, aber es ist nützlich, die drei immer zu unterscheiden.

  1. Python hat Namen und Objekte.
  2. Durch die Zuweisung wird ein Name an ein Objekt gebunden.
  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. Mutabilität ist für diese Frage irrelevant.

Beispiel:

a = 1

Dadurch wird der Name a an ein Objekt vom Typ Integer 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. Der Name b hat danach nichts mehr mit dem Namen x zu tun.

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

In dem in der Frage dargestellten Code bindet die Anweisung self.Change(self.variable) den Namen var (im Funktionsumfang Change ) an das Objekt, das den Wert 'Original' und die Zuweisung var = 'Changed' ( im Hauptteil der Funktion Change ) weist denselben Namen erneut zu: einem anderen Objekt (das auch eine Zeichenfolge enthält, könnte aber auch etwas anderes sein).

Die Python-Dokumentation scheint unklar zu sein, ob Parameter als Referenz oder Wert übergeben werden. 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'

Gibt es etwas, was ich tun kann, um die Variable als Referenz zu übergeben?


(Bearbeiten - Blair hat seine enorm beliebte Antwort so aktualisiert, dass sie jetzt genau ist)

Ich denke, es ist wichtig anzumerken, dass der aktuelle Beitrag mit den meisten Stimmen (von Blair Conrad), obwohl er hinsichtlich des Ergebnisses korrekt ist, irreführend ist und aufgrund seiner Definitionen an der Grenze liegt. Zwar gibt es viele Sprachen (wie C), die es dem Benutzer ermöglichen, entweder als Referenz oder als Wert zu übergeben, Python ist jedoch keine davon.

Die Antwort von David Cournapeau weist auf die tatsächliche Antwort hin und erklärt, warum das Verhalten in Blair Conrads Post richtig zu sein scheint, während die Definitionen nicht zutreffen.

Soweit Python nach Wert übergeben wird, werden alle Sprachen nach Wert übergeben, da ein Teil der Daten (sei es ein "Wert" oder eine "Referenz") gesendet werden muss. Das bedeutet jedoch nicht, dass Python in dem Sinne als Wertübergabe in den Sinn kommt, dass ein C-Programmierer daran denkt.

Wenn Sie das Verhalten wollen, ist Blair Conrads Antwort in Ordnung. Wenn Sie jedoch wissen wollen, warum Python weder Wert noch Referenz ist, lesen Sie die Antwort von David Cournapeau.


Das Problem entsteht durch ein Missverständnis darüber, welche Variablen in Python enthalten sind. Wenn Sie an die meisten traditionellen Sprachen gewöhnt sind, haben Sie ein Denkmodell dafür, was in der folgenden Reihenfolge passiert:

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 funktionieren die Dinge in Python nicht. Stattdessen 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 koexistieren, obwohl a sich nicht mehr auf das erste Objekt bezieht. In der Tat können sie von beliebig vielen anderen 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. Diese unterscheidet sich von der Referenz, die im Funktionsaufruf verwendet wurde. Es gibt also keine Möglichkeit, diese Referenz zu aktualisieren und auf a zu setzen 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 eine zweite Referenzvariable für das Objekt. Innerhalb der Funktion self.variable Sie die Referenzvariable einem anderen String-Objekt 'Changed' , die Referenzvariable self.variable ist jedoch separat und ändert sich nicht.

Die einzige Möglichkeit, dies zu umgehen, besteht darin, ein veränderbares Objekt zu übergeben. Da beide Verweise auf dasselbe Objekt verweisen, werden Änderungen an dem Objekt an beiden Stellen angezeigt.

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

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

Das Python-Pass-by-Assignment-Schema ist nicht ganz das Gleiche wie die Referenzparameteroption von C ++, hat sich jedoch in der Praxis als sehr ähnlich dem Argumentübergabemodell der C-Sprache (und anderer) herausgestellt:

  • Unveränderliche Argumente werden effektiv " nach Wert " übergeben. Objekte wie Ganzzahlen und Strings werden über Objektreferenz und nicht durch Kopieren übergeben. Da Sie jedoch unveränderliche Objekte an Ort und Stelle nicht ändern können, ähnelt der Effekt einer Kopie.
  • Veränderliche Argumente werden effektiv " per Zeiger " übergeben. Objekte wie Listen und Wörterbücher werden auch über die Objektreferenz übergeben. Dies ähnelt der Art, wie C Arrays als Zeiger übergibt. Mutable Objekte können in der Funktion wie C-Arrays geändert werden .

Effbot (alias Fredrik Lundh) hat Pythons variablen Passing-Style als Aufrufobjekt beschrieben: http://effbot.org/zone/call-by-object.htm

Objekte werden auf dem Heap zugewiesen, und Zeiger darauf können an eine beliebige Stelle weitergegeben werden.

  • Wenn Sie eine Zuordnung vornehmen, z. B. x = 1000 , wird ein Wörterbucheintrag erstellt, der die Zeichenfolge "x" im aktuellen Namespace einem Zeiger auf das Integer-Objekt mit eintausend zuordnet.

  • Wenn Sie "x" mit x = 2000 aktualisieren, wird ein neues Ganzzahlobjekt erstellt, und das Wörterbuch wird so aktualisiert, dass es auf das neue Objekt zeigt. Das alte eintausend Objekt ist unverändert (und kann lebendig sein oder 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 Strings und Ganzzahlen sind unveränderlich . Dies bedeutet einfach, dass es keine Methoden gibt, die das Objekt nach der Erstellung ändern können. Sobald das Integer-Objekt eintausend erstellt wurde, wird es beispielsweise nie geändert. Mathematik wird durch Erstellen neuer Ganzzahlobjekte erstellt.

  • Objekte wie Listen sind veränderbar . Dies bedeutet, dass der Inhalt des Objekts durch alles geändert werden kann, das auf das Objekt zeigt. Zum Beispiel x = []; y = x; x.append(10); print y x = []; y = x; x.append(10); print y x = []; y = x; x.append(10); print y druckt [10] . Die leere Liste wurde erstellt. Sowohl "x" als auch "y" zeigen auf dieselbe Liste. Die Append- Methode mutiert (aktualisiert) das Listenobjekt (z. B. 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).

Hoffe das klärt das Problem für dich.


Ein einfacher Trick, den ich normalerweise benutze, ist es 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 unbequem sein, aber manchmal ist es einfach genug, um das zu tun.)


Ich fand die anderen Antworten ziemlich lang und kompliziert, daher erstellte ich dieses einfache Diagramm, um zu erklären, wie Python Variablen und Parameter behandelt.


In diesem Fall wird der Variablen var in der Methode Change eine Referenz auf self.variable , und Sie weisen var sofort einen String 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 , 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 mir sicher, jemand anderes könnte das weiter erklären.


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

Python verwendet immer Referenzwerte. Es gibt keine Ausnahme. Bei jeder Variablenzuweisung 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. Bei der Arbeit mit dem Referenzwert scheinen Sie also direkt mit dem Zielobjekt zu arbeiten. Es gibt jedoch immer eine Referenz dazwischen, einen Schritt mehr, um zum Ziel zu springen.

Hier ist das Beispiel, das beweist, dass Python als Referenz verwendet wird:

Wenn das Argument per Wert übergeben wurde, konnte der äußere lst 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 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 dunkle Gelb ist das interne Wörterbuch. (Es könnte tatsächlich auch als grüne Ellipse gezeichnet werden. Die Farbe und die Form sagt nur, dass es sich um eine innere handelt.)

Sie können die integrierte Funktion id() verwenden, um den Referenzwert (dh die Adresse des Zielobjekts) zu 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 String 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 Wertteil 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. Das heißt, Elemente sind tatsächlich nicht im Container enthalten - nur die Verweise auf Elemente sind vorhanden.


Da Ihr Beispiel zufällig objektorientiert ist, können Sie folgende Änderung 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'

Während Pass by Reference nichts ist, was gut in Python passt und selten verwendet werden sollte, gibt es einige Problemumgehungen, die das aktuelle Objekt einer lokalen Variablen zuweisen oder sogar eine lokale Variable aus einer aufgerufenen Funktion neu zuordnen können.

Die Grundidee ist, eine Funktion zu haben, die diesen Zugriff ausführen kann 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) oder nonlocal(für lokale Variablen in einer Funktion) in einer Wrapper-Funktion.

def change(wrapper):
    wrapper(7)

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

delDieselbe Idee funktioniert beim Lesen und Beenden einer Variablen.

Zum Lesen gibt es noch eine kürzere Möglichkeit, einfach zu verwenden, lambda: xwelche eine aufrufbare zurückgibt, die beim Aufruf den aktuellen Wert von x zurückgibt. Dies ist ein bisschen wie "Anruf mit Namen", der in weit entfernten Sprachen 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

Durch die Unterstützung von Pythons "Reflektion" kann ein Objekt abgerufen werden, das einen Namen / eine Variable in einem bestimmten Bereich erneut zuweisen kann, ohne dass Funktionen explizit in diesem Bereich definiert werden:

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 im übergebenen Wörterbuch in einen Elementzugriff übersetzt. Durch das Übergeben des Ergebnisses des Builtin 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 es scheint für mich zu funktionieren.


Wie Sie feststellen können, benötigen Sie ein veränderbares Objekt, aber lassen Sie mich Ihnen vorschlagen, die globalen Variablen zu überprüfen, da diese Ihnen helfen oder sogar ein solches 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

Es gibt einen kleinen Trick, um ein Objekt als Referenz zu übergeben, obwohl die Sprache dies nicht möglich macht. 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


Hier ist die einfache (ich hoffe) Erklärung des pass by objectin Python verwendeten Konzepts .
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) und nicht die Referenz 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 würde) wird übergeben. Die Funktion change_meversucht also so etwas zu tun:

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

was natürlich 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 dazu führen:

[0, 1].append(2)

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


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)

Viele Einsichten in 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 innerhalb des Funktionskörpers ein neuer Wert zugewiesen wird, wird davon ausgegangen, dass es sich um einen lokalen Wert handelt. Wenn einer Variablen immer 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 ein wenig überraschend ist, wird dies anhand einer kurzen Überlegung erläutert. Auf der einen Seite bietet die Verwendung von global für zugewiesene Variablen einen Balken gegen unbeabsichtigte Nebeneffekte Wenn global jedoch für alle globalen Referenzen erforderlich wäre, würden Sie immer global verwenden. Sie müssten jede Referenz auf eine eingebaute Funktion oder eine Komponente eines importierten Moduls als global deklarieren.Dieses Durcheinander würde die Nützlichkeit der globalen Erklärung zur Identifizierung von Nebenwirkungen zunichte machen. "

Selbst wenn ein veränderliches Objekt an eine Funktion übergeben wird, gilt dies weiterhin. Und erklärt mir deutlich den Grund für das unterschiedliche 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 Zuordnung zu einer globalen Variablen, die nicht als global deklariert ist, erstellt daher ein neues lokales Objekt und bricht die Verknüpfung zum ursprünglichen Objekt.





pass-by-reference