Tässä opetusohjelmassa opimme osoitinta Pythonissa ja näemme, miksi Python ei tue osoitinkonsepteja.
Ymmärrämme myös kuinka voimme simuloida osoitinta Pythonissa. Alla on osoittimen esittely niille, joilla ei ole siitä mitään.
Ymmärrämme myös kuinka voimme simuloida osoitinta Pythonissa. Alla on osoittimen esittely niille, joilla ei ole siitä mitään.
Mikä on Pointer?
Osoitin on erittäin suosittu ja hyödyllinen työkalu muuttujan osoitteen tallentamiseen. Jos joku on koskaan työskennellyt matalan tason kielen kanssa, kuten C . C++ , hän todennäköisesti tuntee osoittimet. Se hallitsee koodia erittäin tehokkaasti. Se voi olla hieman vaikeaa aloittelijoille, mutta se on yksi ohjelman tärkeimmistä käsitteistä. Se voi kuitenkin johtaa erilaisiin muistinhallintavirheisiin. Siten osoittimien määritelmä -
'Osoittimet ovat muuttujia, jotka sisältävät toisen muuttujan muistiosoitteen. Osoitinmuuttujat esitetään tähdellä (*).'
Katsotaanpa seuraava esimerkki osoittimesta C-ohjelmointikielellä.
Esimerkki - Osoittimen käyttäminen C:ssä
#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; }
Lähtö:
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
Sen lisäksi, että osoittimet ovat hyödyllisiä, niitä ei käytetä Python . Tässä aiheessa keskustelemme Pythonin objektimallista ja opimme, miksi Pythonissa ei ole osoittimia. Opimme myös erilaisia tapoja simuloida osoittimia Pythonissa. Ensin keskustellaan siitä, miksi Python ei tue osoittimia.
Miksi Python ei tue osoittimia
Tarkka syy siihen, miksi osoitinta ei tueta, ei ole selvä. Voisiko osoitin olla Pythonissa natiivisti? Pythonin pääkonsepti on sen yksinkertaisuus, mutta osoitin rikkoi Pythonin Zen. Osoittimet ovat pääasiassa suositeltuja implisiittisiä muutoksia pikemminkin kuin eksplisiittisiä muutoksia. Ne ovat myös monimutkaisia, etenkin aloittelijoille.
Osoittimet luovat yleensä monimutkaisuutta koodiin, jossa Python keskittyy pääasiassa käytettävyyteen nopeuden sijaan. Tämän seurauksena Python ei tue osoitinta. Python tarjoaa kuitenkin joitain etuja osoittimen käytöstä.
Ennen kuin ymmärrämme osoittimen Pythonissa, meillä on oltava perusidea seuraavista kohdista.
- Muuttumattomat vs. muuttuvat objektit
- Python-muuttujat/nimet
Objektit Pythonissa
Pythonissa kaikki on objekteja, jopa luokka, funktiot, muuttujat jne. Jokainen objekti sisältää vähintään kolme dataa.
c++ pari
- Viitemäärä
- Tyyppi
- Arvo
Keskustellaan yksitellen.
Viitemäärä - Sitä käytetään muistin hallintaan. Saat lisätietoja Python-muistinhallinnasta lukemalla Pythonin muistinhallinta.
Tyyppi - The CPython kerrosta käytetään tyyppinä varmistamaan tyypin turvallisuus ajon aikana. Lopuksi on arvo, joka on objektiin liittyvä todellinen arvo.
Jos menemme syvemmälle tässä objektissa, huomaamme, etteivät kaikki objektit ole samanlaisia. Tärkeä ero objektityyppien välillä on muuttumaton ja muuttumaton. Ensinnäkin meidän on ymmärrettävä objektityyppien välinen ero, koska se tutkii osoitinta Pythonissa.
Muuttumattomat vs. muuttuvat objektit
Muuttumattomia objekteja ei voi muokata, kun muunnettavia objekteja voidaan muokata. Katsotaan seuraava taulukko yleisistä tyypeistä ja siitä, ovatko ne muuttuvia vai eivät.
Objektit | Tyyppi |
---|---|
Int | Muuttumaton |
Kellua | Muuttumaton |
Bool | Muuttumaton |
Lista | Vaihteleva |
Aseta | Vaihteleva |
Monimutkainen | Vaihteleva |
Tuple | Muuttumaton |
Frozenset | Muuttumaton |
Dict | Vaihteleva |
Voimme tarkistaa yllä olevien objektien tyypin käyttämällä id() menetelmä. Tämä menetelmä palauttaa objektin muistiosoitteen.
Kirjoitamme alla olevia rivejä REPL-ympäristössä.
x = 5 id(x)
Lähtö:
140720979625920
Yllä olevassa koodissa olemme määrittäneet x:lle arvon 10. jos muuttaisimme tätä arvoa korvauksella, saisimme uudet objektit.
x-=1 id(x)
Lähtö:
140720979625888
Kuten näemme, muokkaamme yllä olevaa koodia ja saamme uusia objekteja vastauksena. Otetaan toinen esimerkki str .
git add --all
s = 'java' print(id(s)) s += 'Tpoint' print(s) id(s)
Lähtö:
2315970974512 JavaTpoint 1977728175088
Jälleen muokkaamme x:n arvoa lisäämällä uuden merkkijonon, ja saamme uuden muistiosoitteen. Yritetään lisätä merkkijono suoraan s:ään.
s = 'java' s[0] = T print(id(s))
Lähtö:
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
Yllä oleva koodi palauttaa virheen, se tarkoittaa, että merkkijono ei tue mutaatiota. Niin str on muuttumattomat esineet.
Nyt näemme muuttuvan objektin, kuten luettelon.
my_list = [3, 4, 8] print(id(my_list)) my_list.append(4) print(my_list) print(id(my_list))
Lähtö:
2571132658944 [3, 4, 8, 4] 2571132658944
Kuten yllä olevasta koodista näemme, Minun listani on id alunperin, ja olemme lisänneet luetteloon numerolla 5; Minun listani on sama tunnus, koska luettelo tukee muuntuvuus.
Python-muuttujien ymmärtäminen
Tapa määrittää muuttujat Pythonissa on paljon erilainen kuin C tai C++. Python-muuttuja ei määritä tietotyyppiä. Itse asiassa Pythonilla on nimiä, ei muuttujia.
Joten meidän on ymmärrettävä ero muuttujien ja nimien välillä, ja se on erityisen totta, kun navigoimme Pythonin osoittimien hankalassa aiheessa.
Ymmärretään kuinka muuttuja toimii C:ssä ja miten nimi toimii Pythonissa.
C:n muuttujat
C-kielessä muuttuja on se, että sillä on arvo tai tallennusarvo. Se määritellään tietotyypin mukaan. Katsotaanpa seuraava koodi, joka määrittää muuttujan.
int x = 286;
- Varaa tarpeeksi muistia kokonaisluvulle.
- Annamme tälle muistipaikalle arvon 286.
- X edustaa tätä arvoa.
Jos edustamme näkemystä muistista -
Kuten näemme, x:llä on muistipaikka arvolle 286. Nyt annamme uuden arvon x:lle.
x = 250
Tämä uusi arvo korvaa edellisen arvon. Se tarkoittaa, että muuttuja x on muuttuva.
X:n arvon sijainti on sama, mutta arvo muuttui. Se on merkittävä seikka, joka osoittaa, että x on muistipaikka, ei vain sen nimi.
Nyt esittelemme uuden muuttujan, joka ottaa x:n, sitten y luo uuden muistilaatikon.
int y = x;
Muuttuja y luo uuden laatikon nimeltä y kopioi x:n arvon laatikkoon.
Nimet Pythonissa
Kuten aiemmin keskustelimme, Pythonissa ei ole muuttujia. Sillä on nimet, ja käytämme tätä termiä muuttujina. Mutta muuttujien ja nimien välillä on ero. Katsotaanpa seuraava esimerkki.
x = 289
Yllä oleva koodi hajoaa suorituksen aikana.
- Luo PyObject
- Aseta PyObjectin tyyppikoodiksi kokonaisluku
- Aseta PyObjectin arvoksi 289
- Luo nimi nimeltä x
- Osoita x uuteen PyObjectiin
- Kasvata PyObjectin uudelleenlaskentaa yhdellä
Se näyttää tältä alla.
Pythonissa voimme ymmärtää muuttujan sisäisen toiminnan. Muuttuja x osoittaa objektin viittaukseen, eikä sillä ole muistitilaa kuten ennen. Se näyttää myös, että x = 289 sitoo nimen x viittaukseen.
Nyt esittelemme uuden muuttujan ja annamme sille x:n.
y = x
Pythonissa muuttuja y ei luo uutta objektia; se on vain uusi nimi, joka osoittaa samaan objektiin. Objekti uudelleenlaskenta kasvoi myös yhdellä. Voimme vahvistaa sen seuraavasti.
y is x
Lähtö:
java kääntää merkkijonon
True
Jos lisäämme y:n arvoa yhdellä, se ei enää viittaa samaan objektiin.
y + =1 y is x
Tämä tarkoittaa, että Pythonissa emme määritä muuttujia. Sen sijaan sitomme nimet viittauksiksi.
Osoittimien simulointi Pythonissa
Kuten olemme keskustelleet, Python ei tue osoitinta, mutta voimme hyötyä osoittimen käytöstä. Python tarjoaa vaihtoehtoisia tapoja käyttää osoitinta Pythonissa. Nämä kaksi tapaa esitetään alla.
- Muuttuvien tyyppien käyttäminen osoittimina
- Mukautettujen Python-objektien käyttö
Ymmärretään annetut kohdat.
Muuttuvien tyyppien käyttäminen osoittimena
Edellisessä osiossa olemme määrittäneet muunnettavat objektit; voimme käsitellä niitä ikään kuin ne olisivat osoittimia simuloidaksemme osoittimen käyttäytymistä. Ymmärretään seuraava esimerkki.
C
void add_one(int *a) { *a += 1; }
Yllä olevassa koodissa määritimme osoittimen *a, jonka jälkeen lisäämme arvoa yhdellä. Nyt toteutamme sen main()-funktiolla.
Näyttelijä Rakul Preet Singh
#include int main(void) { int y = 233; printf('y = %d ', y); add_one(&y); printf('y = %d ', y); return 0; }
Lähtö:
y = 233 y = 234
Voimme simuloida tämän tyyppistä käyttäytymistä käyttämällä Python-muuttuvaa tyyppiä. Ymmärrä seuraava esimerkki.
def add_one(x): x[0] += 1 y = [2337] add_one(y) y[0]
Yllä oleva toiminto käyttää luettelon ensimmäistä elementtiä ja lisää sen arvoa yhdellä. Kun suoritamme yllä olevan ohjelman, se tulostaa y:n muokatun arvon. Se tarkoittaa, että voimme replikoida osoittimen käyttämällä muuttuvaa objektia. Mutta jos yritämme simuloida osoitinta muuttumattoman objektin avulla.
z = (2337,) add_one(z)
Lähtö:
Traceback (most recent call last): File '', line 1, in File '', line 2, in add_one TypeError: 'tuple' object does not support item assignment
Käytimme yllä olevan koodin monikkoa, muuttumatonta objektia, joten se palautti virheen. Voimme myös käyttää sanakirjaa osoittimen simulointiin Pythonissa.
Ymmärretään seuraava esimerkki, jossa lasketaan kaikki ohjelmassa esiintyvät toiminnot. Voimme käyttää sanelua saavuttaaksemme tämän.
Esimerkki -
count = {'funcCalls': 0} def car(): count['funcCalls'] += 1 def foo(): count['funCcalls'] += 1 car() foo() count['funcCalls']
Lähtö:
2
Selitys -
Yllä olevassa esimerkissä olemme käyttäneet Kreivi sanakirja, joka piti kirjaa funktiokutsujen määrästä. Kun foo() toimintoa kutsutaan, laskuri kasvaa 2, koska dict on muuttuva.
Python-objektien käyttäminen
Edellisessä esimerkissä olemme käyttäneet dictiä osoittimen emulointiin Pythonissa, mutta joskus on vaikea muistaa kaikkia käytettyjä avainten nimiä. Voimme käyttää Python mukautettua luokkaa sanakirjan sijasta. Ymmärretään seuraava esimerkki.
Esimerkki -
class Pointer(object): def __init__(self): self._metrics = { 'funCalls': 0, 'catPictures': 0, }
Yllä olevassa koodissa olemme määrittäneet Pointer-luokan. Tämä luokka käytti diktaa todellisten tietojen säilyttämiseen _metrics-jäsenmuuttujassa. Se tarjoaa ohjelmallemme muuttuvuuden. Voimme tehdä tämän seuraavasti.
class Pointer(object): # ... @property def funCalls(self): return self._metrics['func_calls'] @property def catPictures_served(self): return self._metrics['cat_pictures_served']
Olemme käyttäneet @omaisuus sisustusarkkitehti. Jos sisustajat eivät ole sinulle tuttuja, käy Python-sisustajan opetusohjelmassamme. @kiinteistön sisustaja käyttää funCalls- ja catPicture_served-palveluita. Nyt luomme Pointer-luokan objektin.
pt = Pointer() pt.funCalls() pt.catPicture_served
Tässä meidän on lisättävä näitä arvoja.
class Pointer(object): # ... def increament(self): self._metrices['funCalls'] += 1 def cat_pics(self): self._metrices['catPictures_served'] += 1
Olemme määrittäneet kaksi uutta menetelmää - increment(), ja cat_pics(). Olemme muokanneet arvoja käyttämällä näitä funktioita matriisien dict. Täällä voimme muuttaa luokkaa samalla tavalla kuin muokkaamme osoitinta.
pt = Pointer() pt.increment() pt.increment() pt.funCalls()
Python ctypes -moduuli
Python ctypes -moduulin avulla voimme luoda C-tyypin osoittimen Pythonissa. Tämä moduuli on hyödyllinen, jos haluamme kutsua funktiokutsua C-kirjastoon, joka vaatii osoittimen. Ymmärretään seuraava esimerkki.
Esimerkki - C-kieli
void incr_one(int *x) { *x += 1; }
Yllä olevassa funktiossa olemme lisänneet x:n arvoa yhdellä. Oletetaan, että tallennamme yllä olevan tiedoston nimeltä incrPointer.c ja tyyppiä seuraavan komennon terminaaliin.
$ gcc -c -Wall -Werror -fpic incrPointer.c $ gcc -shared -o libinc.so incrPointer.o
Ensimmäinen komento kääntää incrPointer.c nimeltään esineeseen incrPointer.o. Toinen komento hyväksyy objektitiedoston ja tuottaa libinic.so tehdäkseen yhteistyötä ctypen kanssa.
esimerkki käyttäjätunnuksesta
import ctypes ## libinc.so library should be same directory as this program lib = ctypes.CDLL('./libinc.so') lib.increment
Lähtö:
Yllä olevassa koodissa ctypes.CDLL palauttaa jaetun objektin nimeltä libinic.so. Se sisältää incrPointer() toiminto. Jos meidän on määritettävä osoitin funktioille, jotka määrittelemme jaetussa objektissa, meidän on määritettävä se käyttämällä ctypes. Katsotaanpa alla olevaa esimerkkiä.
inc = lib.increment ## defining the argtypes inc.argtypes = [ctypes.POINTER(ctypes.c_int)]
Jos kutsumme funktiota käyttämällä eri tyyppiä, se tapahtuu virheen kautta.
incrPointer(10)
Lähtö:
Traceback (most recent call last): File '', line 1, in ctypes.ArgumentError: argument 1: : expected LP_c_int instance instead of int
Tämä johtuu siitä, että incrPointer vaatii osoittimen ja ctypes on tapa siirtää osoitinta Pythonissa.
v = ctypes.c_int(10)
v on C-muuttuja. ctypes tarjoaa menetelmän nimeltä byref() joka ohitti muuttujan viittauksen.
inc(ctypes.byref(a)) a
Lähtö:
c_int(11)
Olemme lisänneet arvoa referenssimuuttujan avulla.
Johtopäätös
Olemme keskustelleet siitä, että osoitinta ei ole Pythonissa, mutta voimme toteuttaa saman käyttäytymisen *mutable-objektin kanssa. Keskustelimme myös ctypes-moduuleista, jotka voivat määrittää C-osoittimen Pythonissa. Olemme määrittäneet muutamia erinomaisia tapoja simuloida osoitinta Pythonissa.