3290 Stimmen

Wie übergebe ich eine Variable per Referenz?

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

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

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

32 Stimmen

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

11 Stimmen

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

79 Stimmen

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

10voto

matino Punkte 16307

Hier die einfache (wie ich hoffe) Erklärung des Konzepts pass by object in Python verwendet.
Wenn Sie ein Objekt an die Funktion übergeben, wird das Objekt selbst übergeben (Objekt in Python ist das, was man in anderen Programmiersprachen einen Wert nennt), nicht der Verweis auf dieses Objekt. Mit anderen Worten, wenn Sie aufrufen:

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 also die Funktion change_me wird versuchen, etwas zu tun wie:

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

was natürlich das an die Funktion übergebene Objekt nicht verändert. Wenn die Funktion wie folgt aussehen würde:

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

Dann würde der Anruf zu einem Ergebnis führen:

[0, 1].append(2)

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

3 Stimmen

Das Problem ist, dass der Auftrag etwas anderes tut, als Sie erwarten. Die list = [1, 2, 3] bewirkt die Wiederverwendung der list Namen für etwas anderes und vergisst das ursprünglich übergebene Objekt. Sie können jedoch versuchen list[:] = [1, 2, 3] (Nebenbei bemerkt list ist der falsche Name für eine Variable. Nachdenken über [0, 1] = [1, 2, 3] ist ein völliger Unsinn. Wie auch immer, was bedeutet Ihrer Meinung nach das Objekt selbst übergeben wird ? Was wird Ihrer Meinung nach auf die Funktion übertragen?

0 Stimmen

@pepr Objekte sind keine Literale. Sie sind Objekte. Die einzige Möglichkeit, über sie zu sprechen, ist, ihnen einen Namen zu geben. Deshalb ist es so einfach, wenn man es einmal begriffen hat, aber enorm kompliziert zu erklären :-)

0 Stimmen

@Veky: Dessen bin ich mir bewusst. Wie auch immer, das Listenliteral wird in das Listenobjekt umgewandelt. Eigentlich kann jedes Objekt in Python ohne Namen existieren, und es kann auch verwendet werden, wenn es keinen Namen hat. Und man kann sie als anonyme Objekte betrachten. Stellen Sie sich vor, dass Objekte die Elemente einer Liste sind. Sie brauchen keinen Namen. Man kann auf sie zugreifen, indem man die Liste indiziert oder durch sie iteriert. Wie auch immer, ich bestehe auf [0, 1] = [1, 2, 3] ist einfach ein schlechtes Beispiel. In Python gibt es nichts dergleichen.

10voto

Dolf Andringa Punkte 1863

Abgesehen von all den großartigen Erklärungen, wie diese Dinge in Python funktionieren, sehe ich keinen einfachen Vorschlag für das Problem. Da Sie anscheinend Objekte und Instanzen erstellen, ist die pythonische Art, Instanzvariablen zu behandeln und sie 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 verweisen Sie normalerweise auf self um auf Instanzattribute zuzugreifen. Es ist normal, die Instanzattribute in __init__ und lesen oder ändern sie in Instanzmethoden. Das ist auch der Grund, warum Sie self als das erste Argument für 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

9voto

Brad Porter Punkte 429

Ich habe die folgende Methode verwendet, um ein paar Fortran-Codes schnell in Python zu konvertieren. Es ist zwar keine Referenzübergabe, wie die ursprüngliche Frage lautete, aber in einigen Fällen ist es eine einfache Lösung.

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

0 Stimmen

Ja, das löst auch das Problem der Referenzübergabe in meinem Anwendungsfall. Ich habe eine Funktion, die im Grunde genommen die Werte in einer dict und gibt dann die dict . Während der Bereinigung kann sich jedoch herausstellen, dass ein Teil des Systems neu aufgebaut werden muss. Daher muss die Funktion nicht nur die bereinigten dict aber auch in der Lage sein, den Wiederaufbau zu signalisieren. Ich habe versucht, eine bool durch Verweis, aber das funktioniert natürlich nicht. Herauszufinden, wie dies zu lösen, fand ich Ihre Lösung (im Grunde ein Tupel zurückgeben) am besten zu arbeiten, während auch nicht ein Hack/Workaround überhaupt (IMHO).

1 Stimmen

@kasimir das ist eines der Dinge, die ich an Python wirklich liebe. Weil es so einfach ist, mehrere Werte als Tupel zurückzugeben, ist es sehr selten, dass man überhaupt eine Referenz übergeben muss.

1 Stimmen

@MarkRansom Ich auch! Ich habe viel in PHP programmiert, und die Übergabe per Referenz ist dort ziemlich üblich, kann aber beim Debuggen sehr mühsam sein. Mit Python kann man das vermeiden, ein weiterer Grund für mich, Python mehr zu lieben :-)

9voto

itmuckel Punkte 1173

Es gibt einen kleinen Trick, um ein Objekt per Referenz zu übergeben, auch wenn die Sprache dies nicht vorsieht. 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 er funktioniert ;-P

0 Stimmen

p ist ein Verweis auf ein veränderbares Listenobjekt, das seinerseits das Objekt obj . Der Verweis "p" wird an changeRef . Innenseite changeRef wird eine neue Referenz erstellt (die neue Referenz heißt ref ), die auf das gleiche Listenobjekt zeigt, das p zeigt auf. Da Listen jedoch veränderbar sind, werden Änderungen an der Liste von beide Referenzen. In diesem Fall haben Sie die ref Referenz, um das Objekt bei Index 0 zu ändern, so dass es anschließend die PassByReference('Michael') Objekt. Die Änderung des Listenobjekts wurde mit ref aber diese Änderung ist sichtbar für p .

0 Stimmen

Also, die Referenzen p y ref zeigen auf ein Listenobjekt, das das einzelne Objekt speichert, PassByReference('Michael') . Daraus folgt, dass p[0].name gibt zurück. Michael . Ja, natürlich, ref ist jetzt außerhalb des Geltungsbereichs und wird möglicherweise in den Müll geworfen, aber das ist egal.

1 Stimmen

Sie haben no die private Instanzvariable geändert, name des Originals PassByReference Objekt, das mit der Referenz verbunden ist obj Allerdings. In der Tat, obj.name wird zurückgegeben Peter . Die vorgenannten Bemerkungen gehen von der Definition Mark Ransom gab.

6voto

Daniel Jour Punkte 15551

Da es nirgends erwähnt zu werden scheint, ist ein Ansatz zur Simulation von Referenzen, wie er z.B. aus C++ bekannt ist, die Verwendung einer "Update"-Funktion und die Übergabe dieser Funktion anstelle der eigentlichen Variable (oder besser gesagt "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 bei "Nur-Out-Referenzen" oder in einer Situation mit mehreren Threads/Prozessen nützlich (indem die Aktualisierungsfunktion thread-/multiprozesssicher gemacht wird).

Offensichtlich erlaubt das oben Gesagte nicht Lesen den Wert, sondern aktualisiert ihn nur.

CodeJaeger.com

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

Powered by:

X