logo

Wskaźnik w Pythonie | Dlaczego Python nie obsługuje wskaźników

W tym samouczku dowiemy się o wskaźnikach w Pythonie i zobaczymy, dlaczego Python nie obsługuje koncepcji wskaźników.

Zrozumiemy również, jak możemy symulować wskaźnik w Pythonie. Poniżej wprowadzenie wskaźnika dla tych, którzy nie mają o tym zielonego pojęcia.

Zrozumiemy również, jak możemy symulować wskaźnik w Pythonie. Poniżej wprowadzenie wskaźnika dla tych co nie mają o tym zielonego pojęcia.

Co to jest wskaźnik?

Wskaźnik jest bardzo popularnym i użytecznym narzędziem do przechowywania adresu zmiennej. Jeśli ktoś kiedykolwiek pracował z językiem niskiego poziomu, np C . C++ , prawdopodobnie byłby zaznajomiony ze wskaźnikami. Bardzo efektywnie zarządza kodem. Może to być nieco trudne dla początkujących, ale jest to jedna z ważnych koncepcji programu. Może to jednak prowadzić do różnych błędów w zarządzaniu pamięcią. Zatem definicja wskaźników -

'Wskaźniki to zmienne przechowujące adres pamięci innej zmiennej. Zmienne wskaźnikowe są oznaczone gwiazdką (*).'

Zobaczmy następujący przykład wskaźnika w języku programowania C.

Przykład - Jak używać wskaźnika w C

 #include int main() { int* po, o; 0 = 10; printf('Address of c: %p
', &c); printf('Value of c: %d

', c); o = &0; printf('Address of pointer pc: %p
', o); printf('Content of pointer pc: %d

', *o); 0 = 11; printf('Address of pointer pc: %p
', p0); printf('Content of pointer pc: %d

', *p0); *po = 2; printf('Address of c: %p
', &o); printf('Value of c: %d

', o); return 0; } 

Wyjście:

Address of o: 2686784 Value of o: 22 Address of pointer po: 2686784 Content of pointer po: 22 Address of pointer po: 2686784 Content of pointer po: 11 Address of o: 2686784 Value of o: 2 

Poza tym, że są przydatne, wskaźniki nie są używane w Pyton . W tym temacie omówimy model obiektowy Pythona i dowiemy się, dlaczego wskaźniki w Pythonie nie istnieją. Poznamy także różne sposoby symulowania wskaźników w Pythonie. Najpierw omówmy, dlaczego Python nie obsługuje wskaźników.

Dlaczego Python nie obsługuje wskaźników

Dokładny powód nieobsługiwania wskaźnika nie jest jasny. Czy wskaźnik w Pythonie może istnieć natywnie? Główną koncepcją Pythona jest jego prostota, ale wskaźnik narusza zasady Zen Pythona. Wskaźniki są zachęcane głównie do zmian ukrytych, a nie jawnych. Są one również skomplikowane, szczególnie dla początkujących.

Wskaźniki mają tendencję do tworzenia złożoności w kodzie, gdzie Python koncentruje się głównie na użyteczności, a nie na szybkości. W rezultacie Python nie obsługuje wskaźników. Jednak Python daje pewne korzyści z używania wskaźnika.

Zanim zrozumiemy wskaźnik w Pythonie, musimy mieć podstawowe pojęcie o poniższych punktach.

  • Obiekty niezmienne i zmienne
  • Zmienne/nazwy Pythona

Obiekty w Pythonie

W Pythonie wszystko jest obiektem, nawet klasa, funkcje, zmienne itp. Każdy obiekt zawiera co najmniej trzy elementy danych.

iterator Java mapy
  • Liczba referencji
  • Typ
  • Wartość

Omówmy jeden po drugim.

Liczba referencji - Służy do zarządzania pamięcią. Aby uzyskać więcej informacji na temat zarządzania pamięcią w Pythonie, przeczytaj Zarządzanie pamięcią w Pythonie.

Typ - The CPyton warstwa jest używana jako typ, aby zapewnić bezpieczeństwo typu w czasie wykonywania. Wreszcie istnieje wartość, która jest rzeczywistą wartością powiązaną z obiektem.

Jeśli jednak zagłębimy się w ten obiekt, odkryjemy, że nie wszystkie obiekty są takie same. Ważne rozróżnienie między typami obiektów jest niezmienne i zmienne. Przede wszystkim musimy zrozumieć różnicę między typami obiektów, ponieważ badają one wskaźnik w Pythonie.

Obiekty niezmienne a zmienne

Obiektów niezmiennych nie można modyfikować, podczas gdy obiekty zmienne można modyfikować. Zobaczmy poniższą tabelę typowych typów i tego, czy można je modyfikować, czy nie.

Obiekty Typ
Wewnętrzne Niezmienny
Platforma Niezmienny
Bool Niezmienny
Lista Zmienny
Ustawić Zmienny
Złożony Zmienny
Krotka Niezmienny
Kraina Lodu Niezmienny
Dykt Zmienny

Rodzaj powyższych obiektów możemy sprawdzić za pomocą metody ID() metoda. Ta metoda zwraca adres pamięci obiektu.

Poniższe linie wpisujemy w środowisku REPL.

 x = 5 id(x) 

Wyjście:

140720979625920 

W powyższym kodzie przypisaliśmy wartość 10 do x. gdybyśmy zmodyfikowali tę wartość poprzez podstawienie, otrzymalibyśmy nowe obiekty.

 x-=1 id(x) 

Wyjście:

140720979625888 

Jak widzimy, modyfikujemy powyższy kod i w odpowiedzi otrzymujemy nowe obiekty. Weźmy inny przykład ul .

 s = 'java' print(id(s)) s += 'Tpoint' print(s) id(s) 

Wyjście:

2315970974512 JavaTpoint 1977728175088 

Ponownie modyfikujemy wartość x, dodając nowy ciąg i otrzymujemy nowy adres pamięci. Spróbujmy dodać ciąg bezpośrednio w s.

 s = 'java' s[0] = T print(id(s)) 

Wyjście:

Traceback (most recent call last): File 'C:/Users/DEVANSH SHARMA/PycharmProjects/MyPythonProject/python1.py', line 34, in s[0] = T NameError: name 'T' is not defined 

Powyższy kod zwraca błąd, oznacza to, że ciąg znaków nie obsługuje mutacji. Więc ul to obiekty niezmienne.

Teraz zobaczymy zmienny obiekt, taki jak lista.

C
 my_list = [3, 4, 8] print(id(my_list)) my_list.append(4) print(my_list) print(id(my_list)) 

Wyjście:

2571132658944 [3, 4, 8, 4] 2571132658944 

Jak widzimy w powyższym kodzie, plik moja lista ma pierwotnie identyfikator i dodaliśmy do listy 5; moja lista ma ten sam identyfikator, ponieważ lista obsługuje zmienność.

Zrozumienie zmiennych Pythona

Sposób definiowania zmiennych w Pythonie znacznie różni się od C czy C++. Zmienna Pythona nie definiuje typu danych. Tak naprawdę Python ma nazwy, a nie zmienne.

Musimy więc zrozumieć różnicę między zmiennymi a nazwami, co jest szczególnie prawdziwe, gdy poruszamy się po trudnym temacie wskaźników w Pythonie.

Przyjrzyjmy się, jak zmienna działa w C i jak nazwa działa w Pythonie.

Zmienne w C

W języku C zmienna przechowuje wartość lub przechowuje wartość. Definiuje się go za pomocą typu danych. Zobaczmy następujący kod definiujący zmienną.

 int x = 286; 
  • Przydziel wystarczającą ilość pamięci dla liczby całkowitej.
  • Przypisujemy wartość 286 do tej lokalizacji pamięci.
  • X oznacza tę wartość.

Jeśli przedstawimy pogląd na pamięć -

Wskaźnik w Pythonie

Jak widzimy, x ma miejsce w pamięci na wartość 286. Teraz przypiszemy nową wartość do x.

x = 250

Ta nowa wartość zastępuje poprzednią wartość. Oznacza to, że zmienna x jest zmienna.

Lokalizacja wartości x jest taka sama, ale wartość uległa zmianie. Jest to istotny punkt wskazujący, że x jest lokalizacją pamięci, a nie tylko jej nazwą.

Teraz wprowadzamy nową zmienną, która przyjmuje x, a następnie y tworzy nowe pudełko pamięci.

 int y = x; 

Zmienna y tworzy nowe pudełko o nazwie y kopiuje wartość z x do pudełka.

Wskaźnik w Pythonie

Nazwy w Pythonie

Jak wspomnieliśmy wcześniej, Python nie ma zmiennych. Ma nazwy i używamy tego terminu jako zmiennych. Istnieje jednak różnica pomiędzy zmiennymi a nazwami. Zobaczmy następujący przykład.

 x = 289 

Powyższy kod jest rozkładany podczas wykonywania.

  1. Utwórz obiekt PyObject
  2. Ustaw kod typu na liczbę całkowitą dla PyObject
  3. Ustaw wartość na 289 dla PyObject
  4. Utwórz nazwę o nazwie x
  5. Wskaż x na nowy obiekt PyObject
  6. Zwiększ przeliczenie obiektu PyObject o 1

Będzie to wyglądało jak poniżej.

ciąg dodany
Wskaźnik w Pythonie

Możemy zrozumieć wewnętrzne działanie zmiennej w Pythonie. Zmienna x wskazuje na referencję obiektu i nie ma już takiej przestrzeni pamięci jak poprzednio. Pokazuje również, że x = 289 wiąże nazwę x z referencją.

Teraz wprowadzamy nową zmienną i przypisujemy do niej x.

 y = x 

W Pythonie zmienna y nie utworzy nowego obiektu; jest to po prostu nowa nazwa wskazująca na ten sam obiekt. Obiekt przeliczenie również wzrosła o jeden. Możemy to potwierdzić w następujący sposób.

 y is x 

Wyjście:

True 

Jeśli zwiększymy wartość y o jeden, nie będzie ono już odnosić się do tego samego obiektu.

 y + =1 y is x 

Oznacza to, że w Pythonie nie przypisujemy zmiennych. Zamiast tego wiążemy nazwy z referencjami.

Symulacja wskaźników w Pythonie

Jak już wspomnieliśmy, Python nie obsługuje wskaźników, ale możemy uzyskać korzyści z używania wskaźnika. Python zapewnia alternatywne sposoby używania wskaźnika w Pythonie. Poniżej podano te dwa sposoby.

  • Używanie typów zmiennych jako wskaźników
  • Używanie niestandardowych obiektów Pythona

Rozumiemy podane punkty.

Używanie typów zmiennych jako wskaźnika

W poprzedniej sekcji zdefiniowaliśmy obiekty typu mutable; możemy je traktować tak, jakby były wskaźnikami symulującymi zachowanie wskaźników. Rozumiemy następujący przykład.

C

 void add_one(int *a) { *a += 1; } 

W powyższym kodzie zdefiniowaliśmy wskaźnik *a, następnie zwiększaliśmy jego wartość o jeden. Teraz zaimplementujemy to za pomocą funkcji main().

jak otworzyć plik json
 #include int main(void) { int y = 233; printf('y = %d
', y); add_one(&y); printf('y = %d
', y); return 0; } 

Wyjście:

y = 233 y = 234 

Możemy symulować tego typu zachowanie, używając typu mutable Pythona. Zrozum następujący przykład.

 def add_one(x): x[0] += 1 y = [2337] add_one(y) y[0] 

Powyższa funkcja uzyskuje dostęp do pierwszego elementu listy i zwiększa jego wartość o jeden. Kiedy wykonamy powyższy program, wypisuje on zmodyfikowaną wartość y. Oznacza to, że możemy replikować wskaźnik za pomocą obiektu zmiennego. Ale jeśli spróbujemy symulować wskaźnik przy użyciu niezmiennego obiektu.

 z = (2337,) add_one(z) 

Wyjście:

Traceback (most recent call last): File '', line 1, in File '', line 2, in add_one TypeError: 'tuple' object does not support item assignment 

W powyższym kodzie użyliśmy krotki, obiektu niezmiennego, więc zwróciła błąd. Możemy również użyć słownika do symulacji wskaźnika w Pythonie.

Rozumiemy następujący przykład, w którym policzymy każdą operację, która wystąpi w programie. Aby to osiągnąć, możemy użyć dict.

Przykład -

 count = {'funcCalls': 0} def car(): count['funcCalls'] += 1 def foo(): count['funCcalls'] += 1 car() foo() count['funcCalls'] 

Wyjście:

2 

Wyjaśnienie -

W powyższym przykładzie użyliśmy metody liczyć słownik, który śledził liczbę wywołań funkcji. Kiedy bla() wywoływana jest funkcja, licznik jest zwiększany o 2, ponieważ dykt jest modyfikowalny.

Używanie obiektów Pythona

W poprzednim przykładzie użyliśmy dict do emulacji wskaźnika w Pythonie, ale czasami trudno jest zapamiętać wszystkie użyte nazwy kluczy. Zamiast słownika możemy użyć niestandardowej klasy Pythona. Rozumiemy następujący przykład.

Przykład -

 class Pointer(object): def __init__(self): self._metrics = { 'funCalls': 0, 'catPictures': 0, } 

W powyższym kodzie zdefiniowaliśmy klasę Pointer. W tej klasie zastosowano dict do przechowywania rzeczywistych danych w zmiennej składowej _metrics. Zapewni to zmienność naszego programu. Możemy to zrobić w następujący sposób.

 class Pointer(object): # ... @property def funCalls(self): return self._metrics['func_calls'] @property def catPictures_served(self): return self._metrics['cat_pictures_served'] 

Użyliśmy @nieruchomość dekorator. Jeśli nie znasz dekoratorów, odwiedź nasz samouczek dotyczący dekoratorów w języku Python. Dekorator @property będzie miał dostęp do funCalls i catPicture_served. Teraz utworzymy obiekt klasy Pointer.

 pt = Pointer() pt.funCalls() pt.catPicture_served 

Tutaj musimy zwiększyć te wartości.

 class Pointer(object): # ... def increament(self): self._metrices['funCalls'] += 1 def cat_pics(self): self._metrices['catPictures_served'] += 1 

Zdefiniowaliśmy dwie nowe metody - inkrement() i cat_pics(). Zmodyfikowaliśmy wartości za pomocą tych funkcji w dyktacie macierzy. Tutaj możemy zmienić klasę w taki sam sposób, w jaki modyfikujemy wskaźnik.

 pt = Pointer() pt.increment() pt.increment() pt.funCalls() 

Moduł ctypes w Pythonie

Moduł ctypes w Pythonie pozwala nam stworzyć wskaźnik typu C w Pythonie. Moduł ten jest pomocny, jeśli chcemy wywołać funkcję do biblioteki C, która wymaga wskaźnika. Rozumiemy następujący przykład.

Przykład — język C

 void incr_one(int *x) { *x += 1; } 

W powyższej funkcji zwiększyliśmy wartość x o jeden. Załóżmy, że zapisujemy powyższy plik o nazwie incrPointer.c i wpisujemy następujące polecenie w terminalu.

 $ gcc -c -Wall -Werror -fpic incrPointer.c $ gcc -shared -o libinc.so incrPointer.o 

Pierwsze polecenie się kompiluje incrPointer.c w obiekt tzw incrPointer.o. Drugie polecenie akceptuje plik obiektowy i tworzy plik libinic.so do współpracy z ctypes.

Konwersja ciągu Java na int
 import ctypes ## libinc.so library should be same directory as this program lib = ctypes.CDLL('./libinc.so') lib.increment 

Wyjście:

 

W powyższym kodzie ctypes.CDLL zwraca współdzielony obiekt o nazwie libinic.so. Zawiera incrPointer() funkcjonować. Jeśli musimy określić wskaźnik do funkcji, które definiujemy w obiekcie współdzielonym, musimy to określić za pomocą ctypes. Zobaczmy poniższy przykład.

 inc = lib.increment ## defining the argtypes inc.argtypes = [ctypes.POINTER(ctypes.c_int)] 

Jeśli wywołamy funkcję przy użyciu innego typu, spowoduje to błąd.

 incrPointer(10) 

Wyjście:

Traceback (most recent call last): File '', line 1, in ctypes.ArgumentError: argument 1: : expected LP_c_int instance instead of int 

Dzieje się tak, ponieważ incrPointer wymaga wskaźnika, a ctypes to sposób przekazywania wskaźnika w Pythonie.

 v = ctypes.c_int(10) 

v jest zmienną C. Typy ctype udostępniają metodę o nazwie byref() który służył do przekazywania odniesienia do zmiennej.

 inc(ctypes.byref(a)) a 

Wyjście:

c_int(11) 

Zwiększyliśmy wartość za pomocą zmiennej referencyjnej.

Wniosek

Omówiliśmy, że wskaźnik nie występuje w Pythonie, ale możemy zaimplementować to samo zachowanie z obiektem *mutable. Omówiliśmy także moduły ctypes, które mogą definiować wskaźnik C w Pythonie. Zdefiniowaliśmy kilka doskonałych sposobów symulowania wskaźnika w Pythonie.