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ęć -
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.
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.
- Utwórz obiekt PyObject
- Ustaw kod typu na liczbę całkowitą dla PyObject
- Ustaw wartość na 289 dla PyObject
- Utwórz nazwę o nazwie x
- Wskaż x na nowy obiekt PyObject
- Zwiększ przeliczenie obiektu PyObject o 1
Będzie to wyglądało jak poniżej.
ciąg dodany
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.