Referencje

W języku Python uzyskujemy dostęp do wartości obiektów poprzez referencjereferencjareferencje. Referencja to nazwa odnosząca się do określonej lokalizacji w pamięci wartości obiektu. Referencje przyjmują często postać zmiennych, atrybutów i elementów.

Zadeklarujmy zmienną x. Kiedy przypiszemy jej wartość 12, nowy obiekt typu int jest alokowany w pamięci, a referencja odwołująca się do niego przypisana jest zmiennej x.

Linia 1. x znak równości 12.

Zadeklarujmy nową zmienną y i przypiszmy jej zmienną x.

Linia 1. x znak równości 12. Linia 2. y znak równości x.

W tym momencie dwie referencje wskazują na obiekt w pamięci przechowujący wartość 12. Aby się o tym przekonać, możemy sprawdzić, do jakiego adresu w pamięci odwołują się referencje – w tym celu użyjemy funkcji id().

Ważne!

W języku Python funkcja id() służy do zwracania identyfikatora obiektu.  Identyfikator obiektu jest unikalnym numerem identyfikacyjnym, który jest przypisany do obiektu podczas jego tworzenia. Każdy obiekt w Pythonie ma swój własny identyfikator, który jest unikatowy dla danego uruchomienia interpretera Pythona.

Linia 1. x znak równości 12 kratka Tworzenie zmiennej x i przypisanie jej wartości 12. Linia 2. y znak równości x kratka Tworzenie zmiennej y i przypisanie jej wartości zmiennej x przecinek co oznacza przecinek że obie zmienne wskazują na ten sam obiekt w pamięci. Linia 4. kratka Sprawdzenie przecinek czy identyfikatory obu zmiennych są takie same przecinek czyli czy wskazują na ten sam obiekt. Linia 5. print otwórz nawias okrągły id otwórz nawias okrągły x zamknij nawias okrągły znak równości znak równości id otwórz nawias okrągły y zamknij nawias okrągły zamknij nawias okrągły kratka Wynik będzie True przecinek ponieważ zarówno x przecinek jak i y wskazują na ten sam obiekt w pamięci. Linia 7. kratka Sprawdzenie przecinek czy obie zmienne x i y wskazują na ten sam obiekt przecinek używając operatora is. Linia 8. print otwórz nawias okrągły x is y zamknij nawias okrągły kratka Wynik będzie True przecinek ponieważ operator is porównuje identyfikatory obiektów przecinek a x i y wskazują na ten sam obiekt.

Zdefiniujemy teraz kolejną zmienną, tym razem o nazwie z. Przypiszemy jej wartość równą 13.

Linia 1. x znak równości 12 kratka Tworzenie zmiennej x i przypisanie jej wartości 12. Linia 2. y znak równości x kratka Tworzenie zmiennej y i przypisanie jej wartości zmiennej x przecinek co oznacza przecinek że obie zmienne wskazują na ten sam obiekt w pamięci. Linia 4. kratka Wyświetlenie identyfikatorów obiektów x i y. Linia 5. print otwórz nawias okrągły id otwórz nawias okrągły x zamknij nawias okrągły zamknij nawias okrągły kratka Wynik będzie 1726522816 przecinek identyfikator obiektu przechowującego wartość 12. Linia 6. print otwórz nawias okrągły id otwórz nawias okrągły y zamknij nawias okrągły zamknij nawias okrągły kratka Wynik będzie 1726522816 przecinek ponieważ zarówno x przecinek jak i y wskazują na ten sam obiekt w pamięci. Linia 8. z znak równości 13 kratka Tworzenie zmiennej z i przypisanie jej wartości 13. Linia 9. kratka Wyświetlenie identyfikatora obiektu z. Linia 10. print otwórz nawias okrągły id otwórz nawias okrągły z zamknij nawias okrągły zamknij nawias okrągły kratka Wynik będzie 1726522848 przecinek identyfikator obiektu przechowującego wartość 13.
Ważne!

Jeśli uruchomisz program na swoim komputerze, wartości id będą inne, ponieważ identyfikator jest przypisywany w momencie utworzenia obiektu, nie zostanie przeniesiony.

W ten sposób w pamięci alokowany jest nowy obiekt przechowujący wartość 13. Zmienna z jest referencją do nowo utworzonego obiektu. Nietrudno zauważyć, że referencje y i z nawiązują do dwóch różnych adresów w pamięci

Sytuacja staje się ciekawsza, jeśli zmiennej y przypiszemy wartość o 1 większą.

Linia 1. x znak równości 12. Linia 2. y znak równości x. Linia 3. print otwórz nawias okrągły id otwórz nawias okrągły x zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 1726522816. Linia 4. print otwórz nawias okrągły id otwórz nawias okrągły y zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 1726522816. Linia 6. z znak równości 13. Linia 7. print otwórz nawias okrągły id otwórz nawias okrągły z zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 1726522848. Linia 9. y znak równości y plus 1. Linia 10. print otwórz nawias okrągły id otwórz nawias okrągły x zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 1726522816. Linia 11. print otwórz nawias okrągły id otwórz nawias okrągły y zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 1726522848. Linia 12. print otwórz nawias okrągły id otwórz nawias okrągły z zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 1726522848.

Możemy zauważyć, że przypisanie zmiennej y wartości 13 sprawiło, że referencja y odwołuje się do tego samego miejsca w pamięci, co referencja z.

Wynika to z tego, że interpreter Python optymalizuje zużycie pamięci. Przypisuje nową referencję do obiektu, jeśli obiekt o tej samej wartości już istnieje w pamięci.

Spójrzmy teraz na inny przykład. Stworzymy dwie różne listy o tej samej zawartości.

Linia 1. liczby znak równości otwórz nawias kwadratowy 2 przecinek 3 przecinek 5 przecinek 7 zamknij nawias kwadratowy. Linia 2. pierwsze znak równości otwórz nawias kwadratowy 2 przecinek 3 przecinek 5 przecinek 7 zamknij nawias kwadratowy.

Kiedy sprawdzimy, do jakich adresów odwołują się referencje liczbypierwsze, zauważymy, że będą to dwie różne lokalizacje.

Linia 1. liczby znak równości otwórz nawias kwadratowy 2 przecinek 3 przecinek 5 przecinek 7 zamknij nawias kwadratowy. Linia 2. pierwsze znak równości otwórz nawias kwadratowy 2 przecinek 3 przecinek 5 przecinek 7 zamknij nawias kwadratowy. Linia 4. print otwórz nawias okrągły id otwórz nawias okrągły liczby zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 2602280317512. Linia 5. print otwórz nawias okrągły id otwórz nawias okrągły pierwsze zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 2602280285384.

Dzieje się tak, ponieważ stworzyliśmy dwa różne obiekty. Co więcej, wyrazy o tych samych wartościach z obu list odwołują się do jednakowych lokalizacji.

Linia 1. liczby znak równości otwórz nawias kwadratowy 2 przecinek 3 przecinek 5 przecinek 7 zamknij nawias kwadratowy. Linia 2. pierwsze znak równości otwórz nawias kwadratowy 2 przecinek 3 przecinek 5 przecinek 7 zamknij nawias kwadratowy. Linia 4. print otwórz nawias okrągły id otwórz nawias okrągły liczby otwórz nawias kwadratowy 0 zamknij nawias kwadratowy zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 1726522496. Linia 5. print otwórz nawias okrągły id otwórz nawias okrągły pierwsze otwórz nawias kwadratowy 0 zamknij nawias kwadratowy zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 1726522496. Linia 7. print otwórz nawias okrągły id otwórz nawias okrągły liczby otwórz nawias kwadratowy 1 zamknij nawias kwadratowy zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 1726522528. Linia 8. print otwórz nawias okrągły id otwórz nawias okrągły pierwsze otwórz nawias kwadratowy 1 zamknij nawias kwadratowy zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 1726522528.

Możemy przypisać referencję liczby do zmiennej referencyjnej pierwsze, wtedy obie zmienne referencyjne będą wskazywać na ten sam adres w pamięci.

Linia 1. liczby znak równości otwórz nawias kwadratowy 2 przecinek 3 przecinek 5 przecinek 7 zamknij nawias kwadratowy. Linia 2. pierwsze znak równości otwórz nawias kwadratowy 2 przecinek 3 przecinek 5 przecinek 7 zamknij nawias kwadratowy. Linia 4. print otwórz nawias okrągły id otwórz nawias okrągły liczby zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 2602280317512. Linia 5. print otwórz nawias okrągły id otwórz nawias okrągły pierwsze zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 2602280285384. Linia 7. pierwsze znak równości liczby. Linia 9. print otwórz nawias okrągły id otwórz nawias okrągły liczby zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 2602280317512. Linia 10. print otwórz nawias okrągły id otwórz nawias okrągły pierwsze zamknij nawias okrągły zamknij nawias okrągły kratka wynik znak równości 2602280317512.
Ważne!

Zwróć uwagę na to, że przy kolejnych wywołaniach wyniki różnią się od siebie. Wynika to z tego, że język Python zarządza alokacją pamięci dla obiektów dynamicznie. Kiedy tworzony jest nowy obiekt (np. liczba całkowita, lista, słownik), język Python przydziela mu miejsce w pamięci. Lokalizacja ta jest określana przez system w momencie tworzenia obiektu i może się różnić za każdym razem, gdy uruchamiasz program.

Przykład 1

Z użyciem referencji tworzy się struktury danych z dowiązaniami. Przykładem takiej struktury jest poznana już wcześniej lista jednokierunkowaPuhk7mD92lista jednokierunkowa, której implementacja w języku Python wygląda następująco:

Linia 1. kratka Definicja klasy reprezentującej pojedynczy węzeł listy jednokierunkowej. Linia 2. class Wezel dwukropek. Linia 3. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self przecinek klucz znak równości None przecinek nastepny podkreślnik wezel znak równości None zamknij nawias okrągły dwukropek. Linia 4. self kropka klucz znak równości klucz kratka Wartość przechowywana w węźle. Linia 5. self kropka nastepny znak równości nastepny podkreślnik wezel kratka Referencja do następnego węzła. Linia 7. kratka Definicja klasy reprezentującej listę jednokierunkową. Linia 8. class ListaJednokierunkowa dwukropek. Linia 9. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self przecinek glowa znak równości None zamknij nawias okrągły dwukropek. Linia 10. self kropka glowa znak równości glowa kratka Początkowy węzeł listy. Linia 12. kratka Dodaje element na początek listy. Linia 13. def dodaj podkreślnik na podkreślnik poczatek otwórz nawias okrągły self przecinek klucz zamknij nawias okrągły dwukropek. Linia 14. nowy podkreślnik wezel znak równości Wezel otwórz nawias okrągły klucz przecinek self kropka glowa zamknij nawias okrągły kratka Tworzy nowy węzeł jako głowę. Linia 15. self kropka glowa znak równości nowy podkreślnik wezel kratka Ustawia nowy węzeł jako głowę listy. Linia 17. kratka Dodaje element na koniec listy. Linia 18. def dodaj podkreślnik na podkreślnik koniec otwórz nawias okrągły self przecinek klucz zamknij nawias okrągły dwukropek. Linia 19. if self kropka glowa is None dwukropek. Linia 20. self kropka glowa znak równości Wezel otwórz nawias okrągły klucz zamknij nawias okrągły kratka Jeśli lista jest pusta przecinek nowy węzeł staje się głową. Linia 21. else dwukropek. Linia 22. biezacy znak równości self kropka glowa. Linia 23. while biezacy kropka nastepny dwukropek kratka Przechodzi przez listę do ostatniego węzła. Linia 24. biezacy znak równości biezacy kropka nastepny. Linia 25. biezacy kropka nastepny znak równości Wezel otwórz nawias okrągły klucz zamknij nawias okrągły kratka Dodaje nowy węzeł na koniec listy. Linia 27. kratka Usuwa i zwraca element z początku listy. Linia 28. def usun podkreślnik z podkreślnik poczatku otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 29. if self kropka glowa dwukropek. Linia 30. usuniety podkreślnik wezel znak równości self kropka glowa kratka Zapamiętuje węzeł do usunięcia. Linia 31. self kropka glowa znak równości self kropka glowa kropka nastepny kratka Ustawia następny węzeł jako nową głowę. Linia 32. usuniety podkreślnik wezel kropka nastepny znak równości None kratka Usuwa referencję do następnego węzła. Linia 33. return usuniety podkreślnik wezel kropka klucz kratka Zwraca wartość usuniętego węzła. Linia 34. else dwukropek. Linia 35. return None. Linia 37. kratka Usuwa i zwraca element z końca listy. Linia 38. def usun podkreślnik z podkreślnik konca otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 39. if self kropka glowa is None dwukropek. Linia 40. return None kratka Lista jest pusta. Linia 41. elif self kropka glowa kropka nastepny is None dwukropek. Linia 42. klucz znak równości self kropka glowa kropka klucz kratka Przypadek jednoelementowej listy. Linia 43. self kropka glowa znak równości None kratka Lista staje się pusta. Linia 44. return klucz. Linia 45. else dwukropek. Linia 46. biezacy znak równości self kropka glowa. Linia 47. while biezacy kropka nastepny kropka nastepny dwukropek kratka Szuka przedostatniego węzła. Linia 48. biezacy znak równości biezacy kropka nastepny. Linia 49. klucz znak równości biezacy kropka nastepny kropka klucz kratka Zapamiętuje wartość ostatniego węzła. Linia 50. biezacy kropka nastepny znak równości None kratka Usuwa ostatni węzeł z listy. Linia 51. return klucz. Linia 53. kratka Reprezentacja listy jako ciągu znaków. Linia 54. def podkreślnik podkreślnik str podkreślnik podkreślnik otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 55. elementy znak równości otwórz nawias kwadratowy zamknij nawias kwadratowy. Linia 56. biezacy znak równości self kropka glowa. Linia 57. while biezacy dwukropek kratka Przechodzi przez listę przecinek dodając klucze do ciągu wynikowego. Linia 58. elementy kropka append otwórz nawias okrągły str otwórz nawias okrągły biezacy kropka klucz zamknij nawias okrągły zamknij nawias okrągły. Linia 59. biezacy znak równości biezacy kropka nastepny. Linia 60. return cudzysłów minus zamknij nawias ostrokątny cudzysłów kropka join otwórz nawias okrągły elementy zamknij nawias okrągły if elementy else cudzysłów Pusta cudzysłów kratka Zwraca łańcuch znaków. Linia 62. kratka Zwraca długość listy. Linia 63. def podkreślnik podkreślnik len podkreślnik podkreślnik otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 64. licznik znak równości 0. Linia 65. biezacy znak równości self kropka glowa. Linia 66. while biezacy dwukropek kratka Liczy elementy w liście. Linia 67. licznik plus znak równości 1. Linia 68. biezacy znak równości biezacy kropka nastepny. Linia 69. return licznik. Linia 71. kratka Demonstracja użycia klasy ListaJednokierunkowa. Linia 72. if podkreślnik podkreślnik name podkreślnik podkreślnik znak równości znak równości apostrof podkreślnik podkreślnik main podkreślnik podkreślnik apostrof dwukropek. Linia 73. liczby znak równości ListaJednokierunkowa otwórz nawias okrągły zamknij nawias okrągły. Linia 74. liczby kropka dodaj podkreślnik na podkreślnik koniec otwórz nawias okrągły 4 zamknij nawias okrągły. Linia 75. liczby kropka dodaj podkreślnik na podkreślnik koniec otwórz nawias okrągły 5 zamknij nawias okrągły. Linia 76. print otwórz nawias okrągły liczby zamknij nawias okrągły kratka Wyświetla dwukropek 4 minus zamknij nawias ostrokątny 5. Linia 78. liczby kropka dodaj podkreślnik na podkreślnik poczatek otwórz nawias okrągły 1 zamknij nawias okrągły. Linia 79. liczby kropka dodaj podkreślnik na podkreślnik poczatek otwórz nawias okrągły 2 zamknij nawias okrągły. Linia 81. print otwórz nawias okrągły zamknij nawias okrągły. Linia 82. print otwórz nawias okrągły liczby zamknij nawias okrągły kratka Wyświetla dwukropek 2 minus zamknij nawias ostrokątny 1 minus zamknij nawias ostrokątny 4 minus zamknij nawias ostrokątny 5. Linia 83. print otwórz nawias okrągły apostrof Długość ciągu dwukropek apostrof przecinek len otwórz nawias okrągły liczby zamknij nawias okrągły zamknij nawias okrągły kratka Wyświetla dwukropek długość dwukropek 4. Linia 85. print otwórz nawias okrągły zamknij nawias okrągły. Linia 86. liczby kropka usun podkreślnik z podkreślnik poczatku otwórz nawias okrągły zamknij nawias okrągły. Linia 87. print otwórz nawias okrągły liczby zamknij nawias okrągły kratka Wyświetla dwukropek 1 minus zamknij nawias ostrokątny 4 minus zamknij nawias ostrokątny 5. Linia 88. print otwórz nawias okrągły apostrof Długość ciągu dwukropek apostrof przecinek len otwórz nawias okrągły liczby zamknij nawias okrągły zamknij nawias okrągły kratka Wyświetla dwukropek długość dwukropek 3. Linia 90. liczby kropka usun podkreślnik z podkreślnik konca otwórz nawias okrągły zamknij nawias okrągły. Linia 91. print otwórz nawias okrągły zamknij nawias okrągły. Linia 92. liczby kropka usun podkreślnik z podkreślnik poczatku otwórz nawias okrągły zamknij nawias okrągły. Linia 93. print otwórz nawias okrągły liczby zamknij nawias okrągły kratka Wyświetla dwukropek 1 minus zamknij nawias ostrokątny 4. Linia 94. print otwórz nawias okrągły apostrof Długość ciągu dwukropek apostrof przecinek len otwórz nawias okrągły liczby zamknij nawias okrągły zamknij nawias okrągły kratka Wyświetla dwukropek długość dwukropek 2. Linia 96. liczby kropka usun podkreślnik z podkreślnik konca otwórz nawias okrągły zamknij nawias okrągły. Linia 97. print otwórz nawias okrągły zamknij nawias okrągły. Linia 98. liczby kropka usun podkreślnik z podkreślnik poczatku otwórz nawias okrągły zamknij nawias okrągły. Linia 99. print otwórz nawias okrągły apostrof Długość ciągu dwukropek apostrof przecinek len otwórz nawias okrągły liczby zamknij nawias okrągły zamknij nawias okrągły kratka Wyświetla dwukropek długość dwukropek 1. Linia 101. liczby kropka usun podkreślnik z podkreślnik konca otwórz nawias okrągły zamknij nawias okrągły.

Program demonstruje działanie listy jednokierunkowej poprzez serię operacji dodawania i usuwania elementów. Używamy klas do reprezentacji struktury danych (listy jednokierunkowej) oraz operacji wykonywanych na tej liście.

Wynik działania programu:

Linia 1. 4 minus zamknij nawias ostrokątny 5. Linia 3. 2 minus zamknij nawias ostrokątny 1 minus zamknij nawias ostrokątny 4 minus zamknij nawias ostrokątny 5. Linia 4. Długość ciągu dwukropek 4. Linia 6. 1 minus zamknij nawias ostrokątny 4 minus zamknij nawias ostrokątny 5. Linia 7. Długość ciągu dwukropek 3. Linia 9. 4. Linia 10. Długość ciągu dwukropek 1. Linia 12. Długość ciągu dwukropek.

Zarządzanie pamięcią

W momencie uruchomienia skryptu języka Python interpreter obsługuje pamięć RAM w określony sposób. Na początku rezerwowana jest pewna określona ilość pamięci potrzebna do wykonania programu. To, jak wielki obszar pamięci będzie zarezerwowany, zależy od systemu operacyjnego, wersji interpretera itp.

Wyróżniamy dwa rodzaje pamięci obsługiwane przez interpreter Python.

  • Stos – pamięć o szybkim dostępie, przechowuje referencje niezbędne do wykonania funkcji. Operacje na stosie wykonywane są w kolejności LIFO. W momencie wywołania funkcji na stosie tworzony jest blok zawierający wartości zmiennych prymitywnychzmienne prymitywnezmiennych prymitywnych i referencji do obiektów – są to parametry wymagane do działania funkcji.

  • Sterta – pamięć o powolnym odczycie i dostępie, zawiera wszystkie utworzone obiekty. Nieużywane obiekty są regularnie sprawdzane i usuwane przez odpowiednie mechanizmy interpretera.

Implementacja Pythona o nazwie CPython ma wbudowane komponenty odpowiadające za usuwanie z pamięci nieużywanych obiektów.

Pierwszym mechanizmem czyszczenia pamięci w CPythonie jest licznik referencji. Za każdym razem, gdy tworzony jest obiekt w Pythonie, bazowy obiekt C ma zarówno typ Pythona, jak i liczbę referencji wskazujących do niego.

Liczba referencji do obiektu w Pythonie jest zwiększana za każdym razem, gdy pojawia się nowa referencja do obiektu. Zmniejsza się natomiast, gdy następuje  dereferencjadereferencjadereferencja. Jeśli liczba odwołań do obiektu wynosi 0, obiekt jest zwalniany z pamięci.

Licznik referencji ma pewne wady, w tym niezdolność do wykrywania cyklicznych referencji. Dzieje się tak, ponieważ licznik referencji w takim przypadku zawsze będzie pokazywać liczbę większą od 0.

Problem ten rozwiązuje odśmiecacz (garbage collectorgarbage collectorgarbage collector), który ma za zadanie przerwać cykliczne referencje.

Słownik

dereferencja
dereferencja

proces pobierania wartości, na którą wskazuje referencja lub odwołanie; Python jest językiem wysokiego poziomu i automatycznie zajmuje się alokacją i dealokacją pamięci, co eliminuje potrzebę bezpośredniego korzystania z referencji; wszystkie zmienne są odwołaniami do obiektów, a operacje, które można by uznać za „dereferencje” w innych językach, są w języku Python operacjami na obiektach; przy przypisaniu zmiennej do innej zmiennej w języku Python przypisywane jest odwołanie do tego samego obiektu, nie powstaje jego kopia:

Linia 1. a znak równości otwórz nawias kwadratowy 1 przecinek 2 przecinek 3 zamknij nawias kwadratowy. Linia 2. b znak równości a.
garbage collector
garbage collector

mechanizm automatycznego zarządzania pamięcią

referencja
referencja

nazwa odnosząca się do określonej lokalizacji w pamięci wartości obiektu

zmienne prymitywne
zmienne prymitywne

zmienne typu prymitywnego przechowują w pamięci konkretne wartości; w języku Python mówimy o typach wbudowanych