Krótka odpowiedź brzmi: Python zawsze przekazuje wartość, ale każda zmienna Pythona jest w rzeczywistości wskaźnikiem do jakiegoś obiektu, więc czasami wygląda jak przekazywanie przez odniesienie.
W Pythonie każdy obiekt jest zmienny lub niezmienny. np. listy, dykty, moduły i ramki danych Pandas są modyfikowalne, a wartości int, stringi i krotki są niezmienne. Obiekty, które można modyfikować, można zmieniać wewnętrznie (np. Dodawać element do listy), ale obiektów niemodyfikowalnych nie można.
Jak powiedziałem na początku, każdą zmienną Pythona można traktować jako wskaźnik do obiektu. Kiedy przekazujesz zmienną do funkcji, zmienna (wskaźnik) w funkcji jest zawsze kopią przekazanej zmiennej (wskaźnika). Jeśli więc przypiszesz coś nowego do zmiennej wewnętrznej, wszystko co robisz to zmiana zmienna lokalna, aby wskazywała na inny obiekt. Nie zmienia to (nie mutuje) oryginalnego obiektu, na który wskazywała zmienna, ani nie powoduje, że zmienna zewnętrzna wskazuje na nowy obiekt. W tym momencie zmienna zewnętrzna nadal wskazuje na oryginalny obiekt, ale zmienna wewnętrzna wskazuje na nowy obiekt.
Jeśli chcesz zmienić oryginalny obiekt (możliwe tylko w przypadku zmiennych typów danych), musisz zrobić coś, co zmieni obiekt bez przypisywania zupełnie nowej wartości do zmiennej lokalnej. Dlatego letgo()
i letgo3()
pozostawić element zewnętrzny niezmienione, ale letgo2()
zmienia go.
Jak zauważył @ursan, gdyby letgo()
zamiast tego użył czegoś takiego, zmieniłby (zmutował) oryginalny obiekt, na który df
wskazuje, co zmieniłoby wartość widzianą przez a
zmienną globalną :
def letgo(df):
df.drop('b', axis=1, inplace=True)
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo(a)
W niektórych przypadkach możesz całkowicie wydrążyć oryginalną zmienną i uzupełnić ją nowymi danymi, bez wykonywania bezpośredniego przypisania, np. Zmieni to oryginalny obiekt, na który v
wskazuje, co zmieni dane widoczne, gdy użyjesz v
później:
def letgo3(x):
x[:] = np.array([[3,3],[3,3]])
v = np.empty((2, 2))
letgo3(v)
Zauważ, że nie przypisuję czegoś bezpośrednio do x
; Przypisuję coś do całego wewnętrznego zakresu x
.
Jeśli absolutnie musisz stworzyć zupełnie nowy obiekt i uczynić go widocznym na zewnątrz (co czasami ma miejsce w przypadku pand), masz dwie możliwości. Opcją „wyczyść” byłoby po prostu zwrócenie nowego obiektu, np.
def letgo(df):
df = df.drop('b',axis=1)
return df
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
a = letgo(a)
Inną opcją byłoby wyjście poza swoją funkcję i bezpośrednia zmiana zmiennej globalnej. Zmieni się to, a
aby wskazać nowy obiekt, a każda funkcja, do której odwołuje się a
później, zobaczy ten nowy obiekt:
def letgo():
global a
a = a.drop('b',axis=1)
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo()
Bezpośrednia zmiana zmiennych globalnych jest zwykle złym pomysłem, ponieważ każdy, kto czyta Twój kod, będzie miał trudności z ustaleniem, w jaki sposób a
został zmieniony. (Generalnie używam zmiennych globalnych dla parametrów współdzielonych używanych przez wiele funkcji w skrypcie, ale nie pozwalam im zmieniać tych zmiennych globalnych).