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?
Argumente sind durch Zuweisung übergeben . Dies hat zwei Gründe:
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.
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.
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 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.
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'
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.
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.
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.
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:
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.
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.)
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.
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.
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).
(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.
(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.
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' )
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:
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.
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!
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
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.
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.
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
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
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
Pass-By-Reference in Python unterscheidet sich erheblich vom Konzept des Pass-By-Reference in C++/Java.
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.
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.
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'
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.
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
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)