To mój drugi dzień nauki Pythona (znam podstawy C ++ i trochę OOP.) I mam pewne zamieszanie dotyczące zmiennych w Pythonie.
Oto jak je obecnie rozumiem:
Zmienne Pythona są referencjami (lub wskaźnikami?) Do obiektów (które są albo zmienne, albo niezmienne). Kiedy mamy coś podobnego num = 5
, niezmienny obiekt 5
jest tworzony gdzieś w pamięci, a para odwołań nazwa-obiekt num
jest tworzona w określonej przestrzeni nazw. Kiedy mamy a = num
, nic nie jest kopiowane, ale teraz obie zmienne odnoszą się do tego samego obiektu ia
są dodawane do tej samej przestrzeni nazw.
To jest, gdy moja książka, Automate the nudne stuff with Python , dezorientuje mnie. Ponieważ jest to książka dla początkujących, nie wspomina o obiektach, przestrzeniach nazw itp. I próbuje wyjaśnić następujący kod:
>>> spam = 42
>>> cheese = spam
>>> spam = 100
>>> spam
100
>>> cheese
42
Wyjaśnienie, które oferuje, jest dokładnie takie samo jak w książce C ++, z czego nie jestem zadowolony, ponieważ mamy do czynienia z odniesieniami / wskaźnikami do obiektów. Więc w tym przypadku myślę, że w trzecim wierszu, ponieważ liczby całkowite są niezmienne, spam
przypisywany jest zupełnie nowy wskaźnik / odniesienie do innej lokalizacji w pamięci, tj. Pamięć, na którą wskazywał początkowo, nie była modyfikowana. Stąd mamy cheese
odniesienie do początkowego obiektu, do którego odnosi się spam
. Czy to właściwe wyjaśnienie?
42
wspam
, teraz przechowywałeśspam
w serze, oznaczacheese = 42
, że po tym zamieniłeśspam
na100
, nie edytowałeścheese
, dlategocheese
nadal jest42
.spam
do numeru 42. Następnie przyklejasz etykietęcheese
do rzeczy oznaczonej jakospam
(nie na samej etykiecie, pamiętaj). Potem zdjąłeś z tegospam
etykietkę i umieściłeś ją na numerze 100.Odpowiedzi:
Jako programista C ++ możesz traktować zmienne Pythona jako wskaźniki.
Zatem kiedy piszesz
spam = 100
, oznacza to, że „przypisujesz wskaźnik”, który poprzednio wskazywał na obiekt42
, aby wskazywał na obiekt100
.Wcześniej
cheese
został przypisany do wskazywania tego samego obiektuspam
, który wskazywał, a który akurat był42
w tym czasie. Ponieważ nie dokonałeś modyfikacjicheese
, nadal wskazuje na42
.Niezmienność nie ma z tym nic wspólnego w tym przypadku, ponieważ przypisanie wskaźnika nie zmienia niczego w wskazywanym obiekcie.
źródło
Z mojego punktu widzenia istnieją różne poglądy na język.
Z punktu widzenia prawnika językowego zmienne Pythona zawsze „wskazują” obiekt. Jednak w przeciwieństwie do Java i C ++ zachowanie == <=> = etc zależy od typu środowiska wykonawczego obiektów, na które wskazują zmienne. Ponadto w Pythonie zarządzanie pamięcią jest obsługiwane przez język.
Z praktycznego punktu widzenia programisty możemy traktować fakt, że liczby całkowite, łańcuchy, krotki itp. Są niezmiennymi * obiektami, a nie wartościami prostymi, jako nieistotny szczegół. Wyjątkiem jest, gdy przechowujemy duże ilości danych liczbowych, możemy chcieć użyć typów, które mogą przechowywać wartości bezpośrednio (np. Tablice numpy), zamiast typów, które kończą się tablicą pełną odwołań do małych obiektów.
Z punktu widzenia implementujących większość języków ma jakąś regułę, która zakłada, że jeśli określone zachowania są poprawne, implementacja jest poprawna, niezależnie od tego, jak rzeczy są faktycznie wykonywane pod maską.
Więc tak, twoje wyjaśnienie jest poprawne z punktu widzenia prawnika językowego. Twoja książka jest poprawna z praktycznego punktu widzenia programisty. To, co faktycznie robi implementacja, zależy od implementacji. W cpythonie liczby całkowite są obiektami rzeczywistymi, chociaż liczby całkowite o małych wartościach są pobierane z puli pamięci podręcznej, a nie tworzone od nowa. Nie jestem pewien, co robią inne implementacje (np. Pypy i jython).
* zwróć uwagę na różnicę między obiektami zmiennymi i niezmiennymi. W przypadku obiektu zmiennego musimy uważać, aby traktować go „jak wartość”, ponieważ jakiś inny kod może go zmodyfikować. W przypadku niezmiennego przedmiotu nie mamy takich obaw.
źródło
Prawdą jest, że jako wskaźniki można w mniejszym lub większym stopniu uwzględniać zmienne. Jednak przykładowy kod bardzo pomógłby w wyjaśnieniu, jak to faktycznie działa.
Po pierwsze, będziemy intensywnie wykorzystywać tę
id
funkcję:Prawdopodobnie zwróci to różne wartości bezwzględne na twoim komputerze.
Rozważmy ten przykład:
>>> foo = 'a string' >>> id(foo) 4565302640 >>> bar = 'a different string' >>> id(bar) 4565321816 >>> bar = foo >>> id(bar) == id(foo) True >>> id(bar) 4565302640
Możesz to zobaczyć:
kiedy zmieniamy wartość foo, przypisujemy mu inny id:
>>> foo = 42 >>> id(foo) 4561661488 >>> foo = 'oh no' >>> id(foo) 4565257832
Ciekawą obserwacją jest również to, że liczby całkowite domyślnie mają tę funkcjonalność do 256:
>>> a = 100 >>> b = 100 >>> c = 100 >>> id(a) == id(b) == id(c) True
Jednak powyżej 256 nie jest to już prawdą:
>>> a = 256 >>> b = 256 >>> id(a) == id(b) True >>> a = 257 >>> b = 257 >>> id(a) == id(b) False
jednak przypisanie
a
dob
rzeczywiście zachowa identyfikator taki sam, jak pokazano wcześniej:>>> a = b >>> id(a) == id(b) True
źródło
Python nie jest przekazywaniem przez odniesienie ani przekazywaniem wartości. Zmienne Pythona nie są wskaźnikami, nie są odniesieniami, nie są wartościami. Zmienne Pythona to nazwy .
Potraktuj to jako „pass-by-alias”, jeśli potrzebujesz tego samego typu frazy lub ewentualnie „pass-by-object”, ponieważ możesz zmutować ten sam obiekt z dowolnej zmiennej, która go wskazuje, jeśli jest zmienna, ale ponowne przypisanie zmienna (alias) zmienia tylko tę jedną zmienną.
Nazwa zmiennej Pythona jest kluczem w globalnej (lub lokalnej) przestrzeni nazw, która w rzeczywistości jest słownikiem. Podstawową wartością jest jakiś obiekt w pamięci. Przypisanie nadaje nazwę temu obiektowi. Przypisanie jednej zmiennej do innej zmiennej oznacza, że obie zmienne są nazwami tego samego obiektu. Ponowne przypisanie jednej zmiennej zmienia nazwę obiektu przez tę zmienną bez zmiany innej zmiennej. Przesunąłeś tag, ale nie zmieniłeś poprzedniego obiektu ani żadnych innych znaczników na nim.
W kodzie C będącym podstawą implementacji CPythona każdy obiekt Pythona jest a
PyObject*
, więc możesz myśleć o nim jak o C, jeśli kiedykolwiek miałeś wskaźniki do danych (bez wskaźników do wskaźników, żadnych bezpośrednio przekazywanych wartości).źródło
Po uruchomieniu
spam = 100
Pythona utwórz w pamięci jeszcze jeden obiekt, ale nie zmieniaj istniejącego. więc nadal masz wskaźnikcheese
do 42 ispam
do 100źródło
To, co dzieje się w
spam = 100
linii, to zamiana poprzedniej wartości (wskaźnik na obiekt typuint
z wartością42
) na inny wskaźnik na inny obiekt (typint
, wartość100
)źródło
new Class()
składni w C ++. Co więcej, w Pythonie wszystko jest instancjąobject
klasy / podklasy.Jak @DeepSpace wspomniał w komentarzach, Ned Batchelder wykonuje świetną robotę, wyjaśniając zmienne (nazwy) i przypisania do wartości na blogu, z którego wygłosił wykład na PyCon 2015, Facts and Myths about Python names and values . Może to być wnikliwe dla Pythonistas na każdym poziomie zaawansowania.
źródło
W Pythonie zmienna przechowuje odniesienie do obiektu . Obiekt to kawał przydzielonej pamięci, która przechowuje wartość i nagłówek . Nagłówek obiektu zawiera jego typ i licznik odwołań, który wskazuje, ile razy ten obiekt jest przywoływany w kodzie źródłowym, aby funkcja Garbage Collection mogła zidentyfikować, czy obiekt może zostać zebrany.
Teraz, kiedy przypisujesz wartości do zmiennej, Python w rzeczywistości przypisuje odniesienia, które są wskaźnikami do lokalizacji pamięci przydzielonych do obiektów:
# x holds a reference to the memory location allocated for # the object(type=string, value="Hello World", refCounter=1) x = "Hello World"
Teraz, gdy przypisujesz obiekty różnego typu do tej samej zmiennej, w rzeczywistości zmieniasz odniesienie tak, aby wskazywało na inny obiekt (tj. Inną lokalizację w pamięci). Do czasu, gdy przypiszesz inne odwołanie (a tym samym obiekt) do zmiennej, Garbage Collector natychmiast odzyska miejsce przydzielone do poprzedniego obiektu, zakładając, że nie odwołuje się do niego żadna inna zmienna w kodzie źródłowym:
# x holds a reference to the memory location allocated for # the object(type=string, value="Hello World", refCounter=1) x = "Hello World" # Now x holds the reference to a different object(type=int, value=10, refCounter=1) # and object(type=string, value="Hello World", refCounter=0) -which is not refereced elsewhere # will now be garbage-collected. x = 10
Przechodząc teraz do twojego przykładu,
spam
przechowuje odniesienie do obiektu (typ = int, wartość = 42, refCounter = 1):>>> spam = 42
Teraz
cheese
będzie również przechowywać odniesienie do obiektu (typ = int, value = 42, refCounter = 2)>>> cheese = spam
Teraz spam zawiera odniesienie do innego obiektu (typ = int, wartość = 100, refCounter = 1)
>>> spam = 100 >>> spam 100
Ale ser będzie nadal wskazywał na obiekt (typ = int, wartość = 42, refCounter = 1)
>>> cheese 42
źródło
Kiedy przechowujesz
spam = 42
, tworzy obiekt w pamięci. Następnie należy przypisaćcheese = spam
to przypisuje obiekt odwołuje sięspam
docheese
. I wreszcie, kiedy się zmieniaszspam = 100
, zmienia się tylkospam
obiekt. A więccheese = 42
.źródło
spam
docheese
. Nie są tworzone żadne nowe obiekty.Strona funkcji numpy.copy () zawiera wyjaśnienie
https://docs.scipy.org/doc/numpy/reference/generated/numpy.copy.html
Przykład, który podaje, jest następujący:
Utwórz tablicę x, z odniesieniem y i kopią z:
x = np.array([1, 2, 3]) y = x z = np.copy(x)
Zwróć uwagę, że kiedy modyfikujemy zmiany x, y, ale nie z:
x[0] = 10 x[0] == y[0] True x[0] == z[0] False
źródło