Wenn ich einen Datenrahmen an eine Funktion übergebe und ihn innerhalb der Funktion ändere, ist es dann eine Wertübergabe oder eine Referenzübergabe?
Ich führe 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 einen Wertübergabe handelt?
Folgendes habe ich auch probiert
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 letgo2()
ändert sich xx
und letgo3()
nicht. Warum ist das so?
Die kurze Antwort lautet, Python führt immer eine Wertübergabe durch, aber jede Python-Variable ist tatsächlich ein Zeiger auf ein Objekt, sodass sie manchmal wie eine Referenzübergabe aussieht.
In Python ist jedes Objekt entweder veränderbar oder nicht veränderbar. zB sind Listen, Diktate, Module und Pandas-Datenrahmen änderbar, und Ints, Strings und Tupel sind nicht änderbar. Veränderbare Objekte können intern geändert werden (zB ein Element zu einer Liste hinzufügen), nicht veränderliche Objekte jedoch nicht.
Wie ich eingangs sagte, können Sie sich jede Python-Variable als Zeiger auf ein Objekt vorstellen. Wenn Sie einer Funktion eine Variable übergeben, ist die Variable (Zeiger) innerhalb der Funktion immer eine Kopie der übergebenen Variablen (Zeiger). Wenn Sie also der internen Variablen etwas Neues zuweisen, ändern Sie nur die lokale Variable, um auf ein anderes Objekt zu verweisen. Dies ändert (mutiert) weder das ursprüngliche Objekt, auf das die Variable zeigte, noch zeigt es die externe Variable auf das neue Objekt. An diesem Punkt zeigt die externe Variable noch auf das ursprüngliche Objekt, aber die interne Variable zeigt auf ein neues Objekt.
Wenn Sie das Originalobjekt ändern möchten (nur bei veränderbaren Datentypen möglich), müssen Sie etwas tun, das das Objekt ändert ohne der lokalen Variablen einen komplett neuen Wert zuweisen. Deshalb letgo()
und letgo3()
Lassen Sie das externe Element unverändert, aber letgo2()
ändert es.
Wie @ursan betonte, wenn letgo()
stattdessen so etwas verwendet, dann würde es das ursprüngliche Objekt ändern (mutieren), das df
zeigt auf, was den über das globale angezeigten Wert ändern würde a
Variable:
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 eine direkte Zuweisung vorzunehmen, z. B. ändert dies das ursprüngliche Objekt, das v
weist darauf hin, wodurch sich die angezeigten Daten ändern, wenn Sie verwenden v
später:
def letgo3(x):
x[:] = np.array([[3,3],[3,3]])
v = np.empty((2, 2))
letgo3(v) # will alter v
Beachten Sie, dass ich nichts direkt zuweise zu x
; Ich ordne etwas dem gesamten internen Bereich von . zu x
.
Wenn Sie unbedingt ein komplett neues Objekt erstellen und nach außen sichtbar machen müssen (was bei Pandas manchmal 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 direkt eine globale Variable zu ändern. Das ändert sich a
um auf ein neues Objekt zu zeigen, und jede Funktion, die auf . verweist a
Danach wird das neue Objekt angezeigt:
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, denn jeder, der Ihren Code liest, wird es schwer haben herauszufinden, wie das geht a
wurde geändert. (Ich verwende im Allgemeinen globale Variablen für gemeinsame Parameter, die von vielen Funktionen in einem Skript verwendet werden, aber ich lasse sie diese globalen Variablen nicht ändern.)
Um @ Mike Grahams Antwort hinzuzufügen, der auf eine sehr gute Lektüre hinwies:
Was in Ihrem Fall wichtig ist, ist der Unterschied zwischen Namen und Werte. a
, df
, xx
, x
, sind alle Namen, aber sie beziehen sich auf dasselbe oder unterschiedliche Werte an verschiedenen Stellen deiner Beispiele:
-
Im ersten Beispiel, letgo
bindet df
auf einen anderen Wert, weil df.drop
gibt ein neues zurück DataFrame
es sei denn, Sie legen das Argument fest inplace = True
(siehe Dokument). Das bedeutet, dass der Name df
(lokal zu den letgo
Funktion), die sich auf den Wert von bezog a
, bezieht sich jetzt auf einen neuen Wert, hier der df.drop
Rückgabewert. Der Wert a
bezieht sich auf existiert noch und hat sich nicht geändert.
-
Im zweiten Beispiel, letgo2
mutiert x
, ohne es neu zu binden, weshalb xx
wird modifiziert von letgo2
. Im Gegensatz zum vorherigen Beispiel ist hier der lokale Name x
bezieht sich immer auf den Wert des Namens xx
bezieht sich auf und ändert diesen Wert an Ort und Stelle, weshalb der Wert xx
sich bezieht, hat sich geändert.
-
Im dritten Beispiel, letgo3
bindet x
zu einem neuen np.array
. Das verursacht den Namen x
, lokal zu letgo3
und zuvor auf den Wert von bezogen xx
, um jetzt auf einen anderen Wert zu verweisen, den neuen np.array
. Der Wert xx
sich bezieht, hat sich nicht geändert.
Die Frage ist nicht PBV vs. PBR. Diese Namen verursachen nur in einer Sprache wie Python Verwirrung; sie wurden für Sprachen erfunden, die wie C oder Fortran funktionieren (als Quintessenz der PBV- und PBR-Sprachen). Es ist wahr, aber nicht aufschlussreich, dass Python immer einen Wert übergibt. Hier stellt sich die Frage, ob der Wert selbst mutiert ist oder ob Sie einen neuen Wert erhalten. Pandas irren normalerweise auf der Seite der letzteren.
http://nedbatchelder.com/text/names.html erklärt sehr gut, was Pythons Namenssystem ist.
Hier ist das Dokument zum Ablegen:
Geben Sie ein neues Objekt mit entfernten Beschriftungen in der angeforderten Achse zurück.
So wird ein neuer Datenrahmen erstellt. Das Original hat sich nicht verändert.
Aber wie bei allen Objekten in Python wird der Datenrahmen per Referenz an die Funktion übergeben.
Sie müssen ‘a’ am Anfang der Funktion global machen, sonst ist es eine lokale Variable und ändert das ‘a’ im Hauptcode nicht.
.
Erläuterungen zu Pythons Modell für die Übergabe nach Zuweisung finden Sie unter Fakten und Mythen über Pythons Namen und Werte, FAQ: Wie schreibe ich eine Funktion mit Ausgabeparametern (Call by Reference)?, SO: Wie übergebe ich eine Variable als Referenz?.
– unutbu
11. August ’16 um 12:11