Próbuję zrozumieć podejście Pythona do zmiennego zakresu. W tym przykładzie, dlaczego jest w f()
stanie zmienić wartość x
postrzeganą wewnątrz main()
, ale nie wartość n
?
def f(n, x):
n = 2
x.append(4)
print('In f():', n, x)
def main():
n = 1
x = [0,1,2,3]
print('Before:', n, x)
f(n, x)
print('After: ', n, x)
main()
Wynik:
Before: 1 [0, 1, 2, 3]
In f(): 2 [0, 1, 2, 3, 4]
After: 1 [0, 1, 2, 3, 4]
Odpowiedzi:
Niektóre odpowiedzi zawierają słowo „kopiuj” w kontekście wywołania funkcji. Uważam to za zagmatwane.
Python nie kopiuje obiektów ty przechodzą podczas wywołania funkcji kiedykolwiek .
Parametry funkcji to nazwy . Kiedy wywołujesz funkcję, Python wiąże te parametry z dowolnymi przekazywanymi obiektami (przez nazwy w zakresie obiektu wywołującego).
Obiekty mogą być zmienne (jak listy) lub niezmienne (jak liczby całkowite, łańcuchy w Pythonie). Zmienny obiekt, który możesz zmienić. Nie możesz zmienić nazwy, po prostu możesz powiązać ją z innym obiektem.
Twój przykład nie dotyczy zakresów ani przestrzeni nazw , ale dotyczy nazewnictwa, wiązania i zmienności obiektu w Pythonie.
Oto ładne zdjęcia przedstawiające różnicę między zmiennymi w innych językach a nazwami w Pythonie .
źródło
def foo(x, l=None): l=l or []; l.append(x**2); return l[-1]
.x = []
inf()
nie ma wpływu na listęx
w funkcji głównej. Zaktualizowałem komentarz, aby był bardziej szczegółowy.Masz już wiele odpowiedzi i zasadniczo zgadzam się z JF Sebastianem, ale może ci się to przydać jako skrót:
Za każdym razem, gdy widzisz
varname =
, tworzysz nowe powiązanie nazwy w zakresie funkcji. Jakakolwiek wartośćvarname
była wcześniej związana, zostaje utracona w tym zakresie .Za każdym razem, gdy widzisz,
varname.foo()
że wywołujesz metodęvarname
. Metoda może zmienić nazwę zmiennej (nplist.append
.).varname
(a raczej obiekt, któregovarname
nazwa) może istnieć w więcej niż jednym zakresie, a ponieważ jest to ten sam obiekt, wszelkie zmiany będą widoczne we wszystkich zakresach.[zwróć uwagę, że
global
słowo kluczowe tworzy wyjątek od pierwszego przypadku]źródło
f
w rzeczywistości nie zmienia wartościx
(która jest zawsze tym samym odwołaniem do instancji listy). Raczej zmienia zawartość tej listy.W obu przypadkach kopia odwołania jest przekazywana do funkcji. Wewnątrz funkcji
n
otrzymuje nową wartość. Modyfikowane jest tylko odwołanie wewnątrz funkcji, a nie poza nią.x
nie otrzymuje nowej wartości: ani referencja wewnątrz, ani na zewnątrz funkcji nie są modyfikowane. Zamiast tego,x
jest to wartość jest modyfikowana.Ponieważ zarówno
x
wewnątrz funkcji, jak i na zewnątrz, odnoszą się do tej samej wartości, obie zobacz modyfikację. Z kolein
wewnątrz funkcji i na zewnątrz odnoszą się do innych wartości pon
ponownym przypisaniu w funkcji.źródło
Zmienię nazwy zmiennych, aby zmniejszyć zamieszanie. n -> nf lub nmain . x -> xf lub xmain :
Kiedy wywołujesz funkcję f , środowisko uruchomieniowe Pythona tworzy kopię xmain i przypisuje ją do xf oraz podobnie przypisuje kopię nmain do nf .
W przypadku n kopiowana wartość to 1.
W przypadku x kopiowana wartość nie jest listą literałów [0, 1, 2, 3] . Jest to odniesienie do tej listy. xf i xmain wskazują na tę samą listę, więc modyfikując xf , modyfikujesz również xmain .
Gdybyś jednak miał napisać coś takiego:
przekonasz się, że xmain się nie zmienił. Dzieje się tak, ponieważ w wierszu xf = ["foo", "bar"] zmieniono xf, aby wskazywał na nową listę. Wszelkie zmiany wprowadzone na nowej liście nie będą miały wpływu na listę, na którą xmain nadal wskazuje.
Mam nadzieję, że to pomoże. :-)
źródło
nf = 2
, w którym nazwanf
zostanie zmieniona na wskazującą2
. Liczby są niezmienne, listy są zmienne.Dzieje się tak, ponieważ lista jest zmiennym obiektem. Nie ustawiasz x na wartość [0,1,2,3], definiujesz etykietę dla obiektu [0,1,2,3].
Powinieneś zadeklarować swoją funkcję f () w ten sposób:
źródło
x = x + [4]
zamiastx.append(4)
, to widać żadnych zmian rozmówcy, a także, choć lista jest zmienny. Ma to związek z tym, czy rzeczywiście jest zmutowany.x += [4]
następniex
jest zmutowany, tak jak co dzieje się zx.append(4)
tak dzwoniący będzie widać zmiany.n jest liczbą int (niezmienną), a kopia jest przekazywana do funkcji, więc w funkcji zmieniasz kopię.
X jest listą (zmienną), a kopia wskaźnika jest przekazywana do funkcji, więc x.append (4) zmienia zawartość listy. Jednak powiedziałeś x = [0,1,2,3,4] w swojej funkcji, nie zmieniłbyś zawartości x w main ().
źródło
Jeśli funkcje są przepisywane z zupełnie innymi zmiennymi i nazywamy je id , to dobrze ilustruje to sprawę. Nie zrozumiałem tego na początku i przeczytałem post jfs ze świetnym wyjaśnieniem , więc starałem się zrozumieć / przekonać siebie:
zi x mają ten sam identyfikator. Tylko różne tagi dla tej samej podstawowej struktury, jak mówi artykuł.
źródło
Python jest czystym językiem przekazywania wartości, jeśli myślisz o tym we właściwy sposób. Zmienna Pythona przechowuje lokalizację obiektu w pamięci. Zmienna Pythona nie przechowuje samego obiektu. Przekazując zmienną do funkcji, przekazujesz kopię adresu obiektu wskazywanego przez zmienną.
Porównaj te dwie funkcje
Teraz, kiedy piszesz do powłoki
Porównaj to z goo.
W pierwszym przypadku przekazujemy kopię adresu krowy do foo i foo modyfikuje stan znajdującego się tam obiektu. Obiekt zostanie zmodyfikowany.
W drugim przypadku przekazujesz kopię adresu krowy do mazi. Następnie goo przystępuje do zmiany tej kopii. Efekt: brak.
Nazywam to zasadą różowego domu . Jeśli skopiujesz swój adres i powiesz malarzowi, żeby pomalował dom pod tym adresem na różowo, skończysz z różowym domem. Jeśli przekażesz malarzowi kopię swojego adresu i powiesz mu, aby zmienił go na nowy adres, adres twojego domu się nie zmieni.
Wyjaśnienie eliminuje wiele nieporozumień. Python przekazuje zmienne adresów przechowywane według wartości.
źródło
Python jest kopiowany przez wartość odniesienia. Obiekt zajmuje pole w pamięci i odniesienie jest skojarzone z tym obiektem, ale samo zajmuje pole w pamięci. Nazwa / wartość jest powiązana z odniesieniem. W funkcji Pythona zawsze kopiuje wartość odwołania, więc w twoim kodzie n jest kopiowane jako nowa nazwa, a kiedy ją przypisujesz, ma nowe miejsce na stosie wywołań. Ale w przypadku listy nazwa również została skopiowana, ale odnosi się do tej samej pamięci (ponieważ nigdy nie przypisujesz liście nowej wartości). To jest magia w Pythonie!
źródło
Moje ogólne zrozumienie jest takie, że każda zmienna obiektowa (taka jak między innymi lista lub dykt) może być modyfikowana za pomocą jej funkcji. Uważam, że nie jesteś w stanie ponownie przypisać parametru - tj. Przypisać go przez odniesienie w ramach wywoływalnej funkcji.
Jest to zgodne z wieloma innymi językami.
Uruchom następujący krótki skrypt, aby zobaczyć, jak to działa:
źródło
Wiele razy modyfikowałem odpowiedź i zdałem sobie sprawę, że nie muszę nic mówić, Python już się wyjaśnił.
Ten diabeł nie jest odniesieniem / wartością / zmienną lub nie / instancją, przestrzenią nazw lub zmienną / listą lub str. TO JEST SKŁADNIA, ZNAK RÓWNOŚCI.
źródło