U ovom vodiču ćemo naučiti o pokazivaču u Pythonu i vidjeti zašto Python ne podržava koncepte pokazivača.
Također ćemo razumjeti kako možemo simulirati pokazivač u Pythonu. Ispod je uvod pokazivača za one koji nemaju ništa o tome.
Također ćemo razumjeti kako možemo simulirati pokazivač u Pythonu. Ispod je uvod pokazivača za one koji nemaju ništa o tome.
Što je Pointer?
Pokazivač je vrlo popularan i koristan alat za pohranjivanje adrese varijable. Ako je netko ikada radio s jezikom niske razine kao što je C . C++ , on/ona bi vjerojatno bili upoznati s pokazivačima. Vrlo učinkovito upravlja kodom. Može biti pomalo teško za početnike, ali to je jedan od važnih koncepata programa. Međutim, to može dovesti do raznih grešaka u upravljanju memorijom. Dakle, definicija pokazivača -
'Pokazivači su varijable koje drže memorijsku adresu druge varijable. Varijable pokazivača predstavljene su zvjezdicom (*).'
Pogledajmo sljedeći primjer pokazivača u C programskom jeziku.
Primjer - Kako koristiti pokazivač u C-u
#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; }
Izlaz:
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
Osim što su korisni, pokazivači se ne koriste u Piton . U ovoj temi raspravljat ćemo o objektnom modelu Pythona i saznati zašto pokazivači u Pythonu ne postoje. Također ćemo naučiti različite načine simulacije pokazivača u Pythonu. Prvo, raspravimo zašto Python ne podržava pokazivače.
Zašto Python ne podržava pokazivače
Točan razlog nepodržavanja pokazivača nije jasan. Može li pokazivač u Pythonu postojati izvorno? Glavni koncept Pythona je njegova jednostavnost, ali pokazivač je prekršio Zen Pythona. Pokazivači se uglavnom potiču na implicitne promjene, a ne na eksplicitne. Također su složeni, posebno za početnike.
Pokazivači obično stvaraju složenost u kodu, gdje se Python uglavnom fokusira na upotrebljivost, a ne na brzinu. Kao rezultat toga, Python ne podržava pokazivač. Međutim, Python daje neke prednosti korištenja pokazivača.
Prije razumijevanja pokazivača u Pythonu, moramo imati osnovnu ideju o sljedećim točkama.
- Nepromjenjivi vs. promjenjivi objekti
- Python varijable/imena
Objekti u Pythonu
U Pythonu je sve objekt, čak i klasa, funkcije, varijable itd. Svaki objekt sadrži najmanje tri dijela podataka.
dvostruki niz java
- Broj referenci
- Tip
- Vrijednost
Raspravljajmo jedno po jedno.
Broj referenci - Koristi se za upravljanje memorijom. Za više informacija o upravljanju memorijom u Pythonu pročitajte Upravljanje memorijom u Pythonu.
Vrsta - The CPython sloj se koristi kao tip kako bi se osigurala sigurnost tipa tijekom izvođenja. Konačno, postoji vrijednost, koja je stvarna vrijednost povezana s objektom.
Ako zađemo dublje u ovaj objekt, otkrit ćemo da nisu svi objekti isti. Važna razlika između tipova objekata je nepromjenjiv i promjenjiv. Prije svega, moramo razumjeti razliku između tipova objekata jer istražuje pokazivač u Pythonu.
Nepromjenjivi u odnosu na promjenjive objekte
Nepromjenjivi objekti se ne mogu mijenjati, dok se promjenjivi objekti mogu mijenjati. Pogledajmo sljedeću tablicu uobičajenih tipova i jesu li promjenjivi ili ne.
Predmeti | Tip |
---|---|
Int | Nepromjenjivo |
Plutati | Nepromjenjivo |
Bool | Nepromjenjivo |
Popis | Promjenjiv |
set | Promjenjiv |
Kompleks | Promjenjiv |
Tuple | Nepromjenjivo |
Frozenset | Nepromjenjivo |
Dict | Promjenjiv |
Tip gore navedenih objekata možemo provjeriti pomoću iskaznica() metoda. Ova metoda vraća memorijsku adresu objekta.
Donje retke upisujemo u REPL okruženju.
x = 5 id(x)
Izlaz:
140720979625920
U gornjem kodu smo x dodijelili vrijednost 10. ako bismo ovu vrijednost modificirali zamjenom, dobili bismo nove objekte.
x-=1 id(x)
Izlaz:
140720979625888
Kao što vidimo, mijenjamo gornji kod i dobivamo nove objekte kao odgovor. Uzmimo još jedan primjer str .
s = 'java' print(id(s)) s += 'Tpoint' print(s) id(s)
Izlaz:
2315970974512 JavaTpoint 1977728175088
Opet, mijenjamo vrijednost x dodavanjem novog niza i dobivamo novu memorijsku adresu. Pokušajmo dodati string izravno u s.
s = 'java' s[0] = T print(id(s))
Izlaz:
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
Gornji kod vraća pogrešku, to znači da niz ne podržava mutaciju. Tako str su nepromjenjivi objekti.
Sada ćemo vidjeti promjenjivi objekt kao što je lista.
niz bajtova u niz java
my_list = [3, 4, 8] print(id(my_list)) my_list.append(4) print(my_list) print(id(my_list))
Izlaz:
2571132658944 [3, 4, 8, 4] 2571132658944
Kao što možemo vidjeti u gornjem kodu, moj_popis ima izvorni id, a mi smo popisu dodali 5; moj_popis ima isti ID jer popis podržava promjenjivost.
Razumijevanje Python varijabli
Način definiranja varijabli u Pythonu znatno je drugačiji nego u C-u ili C++-u. Python varijabla ne definira tip podataka. Zapravo, Python ima imena, a ne varijable.
Dakle, moramo razumjeti razliku između varijabli i imena, a posebno vrijedi kada se krećemo kroz nezgodnu temu pokazivača u Pythonu.
Shvatimo kako varijabla radi u C-u i kako naziv radi u Pythonu.
Varijable u C
U jeziku C, varijabla je da drži vrijednost ili pohranjuje vrijednost. Definira se tipom podataka. Pogledajmo sljedeći kod koji definira varijablu.
int x = 286;
- Dodijelite dovoljno memorije za cijeli broj.
- Toj memorijskoj lokaciji dodjeljujemo vrijednost 286.
- X predstavlja tu vrijednost.
Ako zastupamo pogled sjećanja -
Kao što vidimo, x ima memorijsku lokaciju za vrijednost 286. Sada ćemo x dodijeliti novu vrijednost.
x = 250
Ova nova vrijednost prepisuje prethodnu vrijednost. To znači da je varijabla x promjenjiva.
Lokacija vrijednosti x je ista, ali se vrijednost promijenila. To je značajna točka koja pokazuje da je x memorijska lokacija, a ne samo njezin naziv.
Sada uvodimo novu varijablu koja uzima x, zatim y stvara novu memorijsku kutiju.
int y = x;
Varijabla y stvara novi okvir nazvan y i kopira vrijednost iz x u okvir.
'kruskalov algoritam'
Imena u Pythonu
Kao što smo ranije spomenuli, Python nema varijable. Ima imena, a mi koristimo ovaj izraz kao varijable. Ali postoji razlika između varijabli i imena. Pogledajmo sljedeći primjer.
x = 289
Gornji kod se razgrađuje tijekom izvođenja.
- Stvorite PyObject
- Postavite kod tipa na cijeli broj za PyObject
- Postavite vrijednost na 289 za PyObject
- Napravite ime x
- Pokažite x na novi PyObject
- Povećajte refcount PyObject-a za 1
Izgledat će kao u nastavku.
Možemo razumjeti interni rad varijable u Pythonu. Varijabla x pokazuje na referencu objekta i nema memorijski prostor kao prije. Također pokazuje da x = 289 povezuje ime x s referencom.
Sada uvodimo novu varijablu i dodjeljujemo joj x.
y = x
U Pythonu, varijabla y neće stvoriti novi objekt; to je samo novo ime koje ukazuje na isti objekt. Predmet refcount također povećan za jedan. To možemo potvrditi na sljedeći način.
y is x
Izlaz:
True
Ako povećamo vrijednost y za jedan, više se neće odnositi na isti objekt.
y + =1 y is x
To znači da u Pythonu ne dodjeljujemo varijable. Umjesto toga, vezujemo imena za referencu.
Simulacija pokazivača u Pythonu
Kao što smo spomenuli, Python ne podržava pokazivač, ali možemo iskoristiti prednosti korištenja pokazivača. Python nudi alternativne načine korištenja pokazivača u Pythonu. Ova dva načina navedena su u nastavku.
- Korištenje promjenjivih tipova kao pokazivača
- Korištenje prilagođenih Python objekata
Razumimo dane točke.
Korištenje promjenjivih tipova kao pokazivača
U prethodnom odjeljku definirali smo objekte promjenjivog tipa; možemo ih tretirati kao da su pokazivači za simulaciju ponašanja pokazivača. Razumimo sljedeći primjer.
C
void add_one(int *a) { *a += 1; }
U gornjem kodu definirali smo pokazivač *a, a zatim povećavamo vrijednost za jedan. Sada ćemo to implementirati s funkcijom main().
poništi posljednje uvrštavanje
#include int main(void) { int y = 233; printf('y = %d ', y); add_one(&y); printf('y = %d ', y); return 0; }
Izlaz:
y = 233 y = 234
Ovu vrstu ponašanja možemo simulirati korištenjem Python promjenjivog tipa. Shvatite sljedeći primjer.
def add_one(x): x[0] += 1 y = [2337] add_one(y) y[0]
Gornja funkcija pristupa prvom elementu popisa i povećava njegovu vrijednost za jedan. Kada izvršimo gornji program, on ispisuje modificiranu vrijednost y. To znači da možemo replicirati pokazivač pomoću promjenjivog objekta. Ali ako pokušamo simulirati pokazivač pomoću nepromjenjivog objekta.
z = (2337,) add_one(z)
Izlaz:
Traceback (most recent call last): File '', line 1, in File '', line 2, in add_one TypeError: 'tuple' object does not support item assignment
Koristili smo torku u gornjem kodu, nepromjenjivi objekt, pa je vratio pogrešku. Također možemo koristiti rječnik za simulaciju pokazivača u Pythonu.
Razumimo sljedeći primjer gdje ćemo brojati svaku operaciju koja se dogodi u programu. Možemo koristiti dict da to postignemo.
Primjer -
count = {'funcCalls': 0} def car(): count['funcCalls'] += 1 def foo(): count['funCcalls'] += 1 car() foo() count['funcCalls']
Izlaz:
2
Objašnjenje -
U gornjem primjeru koristili smo računati rječnik, koji je pratio broj poziva funkcija. Kada foo() poziva se funkcija, brojač se povećava 2 jer je dict promjenjiv.
Korištenje Python objekata
U prethodnom smo primjeru upotrijebili dict za oponašanje pokazivača u Pythonu, ali ponekad postaje teško zapamtiti sve korištene nazive ključeva. Možemo koristiti Python prilagođenu klasu umjesto rječnika. Razumimo sljedeći primjer.
Primjer -
class Pointer(object): def __init__(self): self._metrics = { 'funCalls': 0, 'catPictures': 0, }
U gornjem kodu definirali smo klasu Pointer. Ova klasa koristi dict za čuvanje stvarnih podataka u varijabli člana _metrics. Omogućit će promjenjivost našem programu. To možemo učiniti na sljedeći način.
class Pointer(object): # ... @property def funCalls(self): return self._metrics['func_calls'] @property def catPictures_served(self): return self._metrics['cat_pictures_served']
Koristili smo @vlasništvo dekorater. Ako niste upoznati s dekoratorima, posjetite naš vodič za dekoratore Pythona. Dekorater @property pristupit će funkcijama funCalls i catPicture_served. Sada ćemo kreirati objekt klase Pointer.
pt = Pointer() pt.funCalls() pt.catPicture_served
Ovdje moramo povećati ove vrijednosti.
class Pointer(object): # ... def increament(self): self._metrices['funCalls'] += 1 def cat_pics(self): self._metrices['catPictures_served'] += 1
Definirali smo dvije nove metode - increment() i cat_pics(). Promijenili smo vrijednosti pomoću ovih funkcija u matricama dict. Ovdje možemo promijeniti klasu isto kao što mijenjamo pokazivač.
pt = Pointer() pt.increment() pt.increment() pt.funCalls()
Python ctypes modul
Python ctypes modul omogućuje nam stvaranje pokazivača tipa C u Pythonu. Ovaj modul je od pomoći ako želimo uputiti poziv funkciji C biblioteci koja zahtijeva pokazivač. Razumimo sljedeći primjer.
Primjer - jezik C
void incr_one(int *x) { *x += 1; }
U gornjoj funkciji povećali smo vrijednost x za jedan. Pretpostavimo da spremimo gornju datoteku pod nazivom incrPointer.c i upišemo sljedeću naredbu u terminal.
$ gcc -c -Wall -Werror -fpic incrPointer.c $ gcc -shared -o libinc.so incrPointer.o
Prva naredba se kompilira incrPointer.c u objekt tzv incrPointer.o. Druga naredba prihvaća objektnu datoteku i proizvodi libinic.so za suradnju s ctypes.
primjer korisničkog imena
import ctypes ## libinc.so library should be same directory as this program lib = ctypes.CDLL('./libinc.so') lib.increment
Izlaz:
U gornjem kodu, ctypes.CDLL vraća zajednički objekt tzv libinić.tako. Sadrži incrPointer() funkcija. Ako trebamo navesti pokazivač na funkcije koje definiramo u zajedničkom objektu, moramo ga navesti pomoću ctypes. Pogledajmo donji primjer.
inc = lib.increment ## defining the argtypes inc.argtypes = [ctypes.POINTER(ctypes.c_int)]
Ako pozovemo funkciju pomoću drugog tipa, to će se dogoditi kroz pogrešku.
incrPointer(10)
Izlaz:
Traceback (most recent call last): File '', line 1, in ctypes.ArgumentError: argument 1: : expected LP_c_int instance instead of int
To je zato što incrPointer zahtijeva pokazivač, a ctypes je način prosljeđivanja pokazivača u Pythonu.
v = ctypes.c_int(10)
v je C varijabla. Ctypes pruža metodu tzv byref() koji je koristio za prosljeđivanje reference varijable.
inc(ctypes.byref(a)) a
Izlaz:
c_int(11)
Povećali smo vrijednost pomoću referentne varijable.
Zaključak
Raspravili smo da pokazivač nije prisutan u Pythonu, ali možemo implementirati isto ponašanje s objektom *mutable. Također smo razgovarali o ctypes modulima koji mogu definirati C pokazivač u Pythonu. Definirali smo nekoliko izvrsnih načina za simulaciju pokazivača u Pythonu.