Wenn ich einen Datenrahmen an eine Funktion übergebe und ihn innerhalb der Funktion ändere, ist er dann Wertübergabe oder Referenzübergabe?
Ich führe den folgenden Code aus
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
def letgo(df):
df = df.drop('b',axis=1)
letgo(a)
Der Wert von a
ändert sich nach dem Funktionsaufruf nicht. Bedeutet das, dass es sich um eine Wertübergabe handelt?
Ich habe auch folgendes versucht
xx = np.array([[1,2], [3,4]])
def letgo2(x):
x[1,1] = 100
def letgo3(x):
x = np.array([[3,3],[3,3]])
Es stellt sich heraus, dass sich letgo2()
dies ändert xx
und letgo3()
nicht. Warum ist es so?
Antworten:
Die kurze Antwort lautet: Python führt immer einen Wert durch, aber jede Python-Variable ist tatsächlich ein Zeiger auf ein Objekt, sodass es manchmal wie ein Referenzübergang aussieht.
In Python ist jedes Objekt entweder veränderbar oder nicht veränderbar. Beispielsweise sind Listen, Dikte, Module und Pandas-Datenrahmen veränderbar, und Ints, Strings und Tupel sind nicht veränderbar. Veränderbare Objekte können intern geändert werden (z. B. ein Element zu einer Liste hinzufügen), nicht veränderbare Objekte jedoch nicht.
Wie ich zu Beginn sagte, können Sie sich jede Python-Variable als Zeiger auf ein Objekt vorstellen. Wenn Sie eine Variable an eine Funktion übergeben, ist die Variable (Zeiger) innerhalb der Funktion immer eine Kopie der übergebenen Variablen (Zeiger). Wenn Sie der internen Variablen also etwas Neues zuweisen, ändern Sie lediglich die lokale Variable, die auf ein anderes Objekt verweist. Dies ändert (mutiert) weder das ursprüngliche Objekt, auf das die Variable zeigte, noch lässt es die externe Variable auf das neue Objekt zeigen. Zu diesem Zeitpunkt zeigt die externe Variable weiterhin auf das ursprüngliche Objekt, die interne Variable jedoch auf ein neues Objekt.
Wenn Sie das ursprüngliche Objekt ändern möchten (nur mit veränderlichen Datentypen möglich), müssen Sie etwas tun, das das Objekt ändert, ohne der lokalen Variablen einen völlig neuen Wert zuzuweisen. Aus diesem Grund
letgo()
undletgo3()
lassen Sie das externe Element unverändert, aberletgo2()
verändert es.Wie @ursan betonte, würde bei
letgo()
Verwendung von so etwas das ursprüngliche Objekt,df
auf das verwiesen wird, geändert (mutiert) , wodurch sich der über die globalea
Variable angezeigte Wert ändern würde :def letgo(df): df.drop('b', axis=1, inplace=True) a = pd.DataFrame({'a':[1,2], 'b':[3,4]}) letgo(a) # will alter a
In einigen Fällen können Sie die ursprüngliche Variable vollständig aushöhlen und mit neuen Daten füllen, ohne tatsächlich eine direkte Zuweisung vorzunehmen. Dies ändert beispielsweise das ursprüngliche Objekt,
v
auf das verwiesen wird, und ändert die Daten, die bei einerv
späteren Verwendung angezeigt werden:def letgo3(x): x[:] = np.array([[3,3],[3,3]]) v = np.empty((2, 2)) letgo3(v) # will alter v
Beachten Sie, dass ich etwas nicht direkt zuordne
x
; Ich ordne dem gesamten internen Bereich von etwas zux
.Wenn Sie unbedingt ein völlig neues Objekt erstellen und extern sichtbar machen müssen (was manchmal bei Pandas der Fall ist), haben Sie zwei Möglichkeiten. Die 'saubere' Option wäre nur, das neue Objekt zurückzugeben, z.
def letgo(df): df = df.drop('b',axis=1) return df a = pd.DataFrame({'a':[1,2], 'b':[3,4]}) a = letgo(a)
Eine andere Möglichkeit wäre, außerhalb Ihrer Funktion zu greifen und eine globale Variable direkt zu ändern. Dies ändert sich
a
, um auf ein neues Objekt zu verweisen, und jede Funktion, die sicha
danach darauf bezieht, sieht dieses neue Objekt:def letgo(): global a a = a.drop('b',axis=1) a = pd.DataFrame({'a':[1,2], 'b':[3,4]}) letgo() # will alter a!
Das direkte Ändern globaler Variablen ist normalerweise eine schlechte Idee, da jeder, der Ihren Code liest, Schwierigkeiten hat, herauszufinden, wie er
a
geändert wurde. (Ich verwende im Allgemeinen globale Variablen für gemeinsam genutzte Parameter, die von vielen Funktionen in einem Skript verwendet werden, aber ich lasse nicht zu, dass sie diese globalen Variablen ändern.)quelle
Die Frage ist nicht PBV vs. PBR. Diese Namen sorgen nur in einer Sprache wie Python für Verwirrung. Sie wurden für Sprachen erfunden, die wie C oder Fortran funktionieren (als Inbegriff von PBV- und PBR-Sprachen). Es ist wahr, aber nicht aufschlussreich, dass Python immer nach Wert geht. Die Frage ist hier, ob der Wert selbst mutiert ist oder ob Sie einen neuen Wert erhalten. Pandas irren normalerweise auf der Seite des letzteren.
http://nedbatchelder.com/text/names.html erklärt sehr gut, was Pythons Namenssystem ist.
quelle
Um die Antwort von @Mike Graham zu ergänzen, der auf eine sehr gute Lektüre hinwies:
In Ihrem Fall ist es wichtig, sich an den Unterschied zwischen Namen und Werten zu erinnern .
a
,df
,xx
,x
, Sind alle Namen , aber sie beziehen sich auf die gleichen oder unterschiedliche Werte an verschiedenen Stellen Ihrer Beispiele:Im ersten Beispiel wird
letgo
erneutdf
an einen anderen Wert gebunden , dadf.drop
ein neuer Wert zurückgegeben wird,DataFrame
sofern Sie das Argument nicht festlegeninplace = True
( siehe Dokument ). Das bedeutet, dass der Namedf
(lokal für dieletgo
Funktion), der sich auf den Wert von bezoga
, sich jetzt auf einen neuen Wert bezieht, hier dendf.drop
Rückgabewert. Der Wert, auf dena
sich bezieht, ist noch vorhanden und hat sich nicht geändert.Im zweiten Beispiel
letgo2
mutiertx
, ohne es erneut zu binden, weshalbxx
wird durch modifiziertletgo2
. Im Gegensatz zum vorherigen Beispielx
bezieht sich der lokale Name hier immer auf den Wert, auf den sich der Namexx
bezieht, und ändert diesen Wert an Ort und Stelle , weshalb sich der Wert, auf den erxx
sich bezieht, geändert hat.Im dritten Beispiel wird
letgo3
erneutx
an ein neues gebundennp.array
. Dies bewirktx
, dass der Name , der lokal aufletgo3
den Wert vonxx
new verweist und sich zuvor auf diesen bezieht , jetzt auf einen anderen Wert verweist, den neuennp.array
. Der Wert, auf denxx
sich bezieht, hat sich nicht geändert.quelle
Python wird weder als Wert noch als Referenz übergeben. Es wird durch Zuordnung übergeben.
Unterstützende Referenz, die Python-FAQ: https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference
IOW:
Wenn Sie also eine Liste übergeben und ihren 0. Wert ändern, wird diese Änderung sowohl im angerufenen als auch im Anrufer angezeigt. Wenn Sie die Liste jedoch einer neuen Liste zuweisen, geht diese Änderung verloren. Wenn Sie jedoch die Liste aufteilen und durch eine neue Liste ersetzen , wird diese Änderung sowohl beim angerufenen als auch beim anrufenden Benutzer angezeigt.
Z.B:
def change_it(list_): # This change would be seen in the caller if we left it alone list_[0] = 28 # This change is also seen in the caller, and replaces the above # change list_[:] = [1, 2] # This change is not seen in the caller. # If this were pass by reference, this change too would be seen in # caller. list_ = [3, 4] thing = [10, 20] change_it(thing) # here, thing is [1, 2]
Wenn Sie ein C-Fan sind, können Sie sich vorstellen, dass Sie einen Zeiger nach Wert übergeben - nicht einen Zeiger auf einen Zeiger auf einen Wert, sondern nur einen Zeiger auf einen Wert.
HTH.
quelle
Hier ist das Dokument zum Löschen:
So wird ein neuer Datenrahmen erstellt. Das Original hat sich nicht geändert.
Wie bei allen Objekten in Python wird der Datenrahmen als Referenz an die Funktion übergeben.
quelle
df
innerhalb der Funktion zugewiesen. Bedeutet das nicht, dass der referenzierte Wert in das neue Objekt geändert wurde?Sie müssen 'a' zu Beginn der Funktion global machen, da es sich sonst um eine lokale Variable handelt und das 'a' im Hauptcode nicht ändert.
quelle