Python Pandas Datenrahmen, ist es Pass-by-Value oder Pass-by-Reference

84

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 xxund letgo3()nicht. Warum ist es so?

nr
quelle

Antworten:

91

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()und letgo3()lassen Sie das externe Element unverändert, aber letgo2()verändert es.

Wie @ursan betonte, würde bei letgo()Verwendung von so etwas das ursprüngliche Objekt, dfauf das verwiesen wird, geändert (mutiert) , wodurch sich der über die globale aVariable 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, vauf das verwiesen wird, und ändert die Daten, die bei einer vspä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 zu x.

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 sich adanach 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 ageä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.)

Matthias Fripp
quelle
7

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.

Mike Graham
quelle
1
Die Semantik des Übergebens und Zuweisens in Python ist genau die gleiche wie in Java, und die gleichen Dinge, die Sie sagen, können auch auf Java angewendet werden. Bei StackOverflow und anderswo im Internet finden es die Leute anscheinend "aufschlussreich", Sie davon zu überzeugen, dass Java immer als Wert übergeben wird, wenn dieses Problem auftritt.
Newacct
7

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 erneut df an einen anderen Wert gebunden , da df.dropein neuer Wert zurückgegeben wird, DataFramesofern Sie das Argument nicht festlegen inplace = True( siehe Dokument ). Das bedeutet, dass der Name df(lokal für die letgoFunktion), der sich auf den Wert von bezog a, sich jetzt auf einen neuen Wert bezieht, hier den df.dropRückgabewert. Der Wert, auf den asich bezieht, ist noch vorhanden und hat sich nicht geändert.

  • Im zweiten Beispiel letgo2 mutiert x , ohne es erneut zu binden, weshalb xxwird durch modifiziert letgo2. Im Gegensatz zum vorherigen Beispiel xbezieht sich der lokale Name hier immer auf den Wert, auf den sich der Name xxbezieht, und ändert diesen Wert an Ort und Stelle , weshalb sich der Wert, auf den er xxsich bezieht, geändert hat.

  • Im dritten Beispiel wird letgo3 erneut x an ein neues gebundennp.array . Dies bewirkt x, dass der Name , der lokal auf letgo3den Wert von xxnew verweist und sich zuvor auf diesen bezieht , jetzt auf einen anderen Wert verweist, den neuen np.array. Der Wert, auf den xxsich bezieht, hat sich nicht geändert.

ursan
quelle
3

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:

  1. Wenn Sie einen unveränderlichen Wert übergeben, ändern Änderungen daran seinen Wert im Aufrufer nicht, da Sie den Namen erneut an ein neues Objekt binden.
  2. Wenn Sie einen veränderlichen Wert übergeben, ändern Änderungen, die in der aufgerufenen Funktion vorgenommen wurden, auch den Wert im Aufrufer, solange Sie diesen Namen nicht an ein neues Objekt binden. Wenn Sie die Variable neu zuweisen und ein neues Objekt erstellen, werden diese Änderung und nachfolgende Änderungen des Namens im Aufrufer nicht angezeigt.

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.

Dstromberg
quelle
0

Hier ist das Dokument zum Löschen:

Neues Objekt mit entfernten Beschriftungen in der angeforderten Achse zurückgeben.

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.

Israel Unterman
quelle
Aber ich habe es dfinnerhalb der Funktion zugewiesen. Bedeutet das nicht, dass der referenzierte Wert in das neue Objekt geändert wurde?
Nr.
Durch das Zuweisen zu einem lokalen Namen wird niemals geändert, an welches Objekt ein Name in einem anderen Bereich gebunden ist.
Mike Graham
0

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.

Zosan
quelle