ise Pythonis, demüstifitseeritud

Kui olete juba mõnda aega Pythonis (objektorienteeritud programmeerimine) programmeerinud, olete kindlasti kokku puutunud meetoditega, mille selfesimene parameeter on.

Püüdkem kõigepealt mõista, mis see korduv iseparameeter on.

Mis on Pythonis mina?

Objektikeskse programmeerimise korral kasutame alati klassi selfparameetreid määrates igal juhul esimese parameetrina. Vaatame klassi nimetust Cat.

 class Cat: def __init__(self, name, age): self.name = name self.age = age def info(self): print(f"I am a cat. My name is (self.name). I am (self.age) years old.") def make_sound(self): print("Meow")

Sel juhul on kõigil meetoditel, sealhulgas __init__ka esimene parameeter self.

Me teame, et klass on objektide kavand. Selle kavandi abil saab luua mitu arvu objekte. Loome ülaltoodud klassist kaks erinevat objekti.

 cat1 = Cat('Andy', 2) cat2 = Cat('Phoebe', 3)

selfMärksõna on kasutatud esindama näiteks (objekti) antud klassi. Sel juhul on kaks Catobjekti cat1ja cat2neil on oma nameja ageatribuudid. Kui eneseavaldust ei olnud, ei suutnud sama klass mõlema objekti kohta teavet hoida.

Kuid kuna klass on vaid plaan, selfvõimaldab see juurdepääsu pythoni iga objekti atribuutidele ja meetoditele. See võimaldab igal objektil olla oma atribuudid ja meetodid. Seega, isegi ammu enne nende objektide loomist, viitame objektidele selfklassi määratlemisel.

Miks on ennast iga kord selgesõnaliselt määratletud?

Isegi kui mõistame selle kasutamist self, võib see siiski tunduda veider, eriti teistest keeltest pärit programmeerijatele, mis selfedastatakse parameetrina selgesõnaliselt iga kord, kui me meetodi määratleme. Nagu The Py of Python ütleb: " Selgesõnaline on parem kui kaudne ".

Miks me peame seda tegema? Võtame alustuseks lihtsa näite. Meil on Pointklass, mis määratleb meetodi distancepäritolu kauguse arvutamiseks.

 class Point(object): def __init__(self,x = 0,y = 0): self.x = x self.y = y def distance(self): """Find distance from origin""" return (self.x**2 + self.y**2) ** 0.5

Instantsime nüüd selle klassi ja leidkem vahemaa.

 >>> p1 = Point(6,8) >>> p1.distance() 10.0

Ülaltoodud näites __init__()määratletakse kolm parameetrit, kuid me lihtsalt läbisime kaks (6 ja 8). Samamoodi distance()nõuab üks, kuid null argumenti edastati. Miks Python selle argumentide arvu mittevastavuse üle ei kurda?

Mis toimub sisemiselt?

Point.distanceja p1.distanceülaltoodud näites on erinevad ja mitte täpselt ühesugused.

 >>> type(Point.distance) >>> type(p1.distance) 

Näeme, et esimene on funktsioon ja teine ​​meetod. Meetodite (Pythonis) eripära on see, et objekt ise edastatakse esimese argumendina vastavale funktsioonile.

Ülaltoodud näite puhul on meetodi väljakutse p1.distance()tegelikult samaväärne Point.distance(p1).

Üldiselt, kui nimetame meetodit mõne argumendiga, kutsutakse vastav klassifunktsioon, asetades meetodi objekti esimese argumendi ette. Niisiis, kõike muud obj.meth(args)saab Class.meth(obj, args). Helistamisprotsess on automaatne, samas kui vastuvõtuprotsess pole (selgesõnaline).

Seetõttu peab klassi funktsiooni esimene parameeter olema objekt ise. Selle parameetri kirjutamine selfon lihtsalt kokkulepe. See ei ole märksõna ega oma Pythonis erilist tähendust. Võiksime kasutada muid nimesid (näiteks this), kuid see on väga soovitatav. Muude nimede kasutamine selfpeale selle on enamiku arendajate poolt pahaks pandud ja halvendab koodi loetavust ( loetavus loeb ).

Minast saab hoiduda

Nüüdseks on teil selge, et objekt (eksemplar) edastatakse automaatselt esimese argumendina. Seda kaudset käitumist saab staatilise meetodi tegemisel vältida . Vaatleme järgmist lihtsat näidet:

 class A(object): @staticmethod def stat_meth(): print("Look no self was passed")

Siin @staticmethodon funktsiooni dekoraator, mis muudab stat_meth()staatilise. Instantsime selle klassi ja nimetame meetodit.

 >>> a = A() >>> a.stat_meth() Look no self was passed

Ülaltoodud näite põhjal näeme, et staatilise meetodi kasutamisel välditi objekti esimese argumendina edastamise kaudset käitumist. Kokkuvõttes käituvad staatilised meetodid nagu tavalised vanad funktsioonid (kuna kõik klassi objektid jagavad staatilisi meetodeid).

 >>> type(A.stat_meth) >>> type(a.stat_meth) 

Ise on siin, et jääda

Selgesõnaline selfpole Pythonile ainuomane. See idee on laenatud Modula-3-st . Järgmine on kasutusjuht, kus sellest saab abi.

Pythonis pole selgesõnalist muutujadeklaratsiooni. Nad hakkavad tegutsema esimesel ülesandel. Kasutamine selfhõlbustab eksemplari atribuutide (ja meetodite) eristamist kohalikest muutujatest.

Esimeses näites on self.x eksemplari atribuut, samas kui x on kohalik muutuja. Need ei ole ühesugused ja nad asuvad erinevates nimeruumides.

Paljud on teinud ettepaneku muuta ise Pythonis märksõnaks, näiteks thisC ++ ja Java. See välistaks selfmeetodites formaalse parameetrite loendi sõnaselge kasutamise.

Kuigi see idee näib paljutõotav, ei juhtu seda. Vähemalt mitte lähitulevikus. Peamine põhjus on tagasiulatuv ühilduvus. Siin on Pythoni looja enda ajaveeb, kus selgitatakse, miks selgesõnaline mina peab jääma.

__init __ () ei ole konstruktor

Senise teabe põhjal saab teha ühe olulise järelduse, et __init__()meetod ei ole konstruktor. Paljud naiivsed Pythoni programmeerijad lähevad sellega segadusse, kuna neid __init__()kutsutakse objekti loomisel.

Lähemal uurimisel selgub, et esimene parameeter __init__()on objekt ise (objekt on juba olemas). Funktsioon __init__()kutsutakse kohe pärast objekti loomist ja seda kasutatakse selle initsialiseerimiseks.

Tehniliselt öeldes on konstruktor meetod, mis loob objekti ise. Pythonis on see meetod __new__(). Selle meetodi tavaline allkiri on:

 __new__(cls, *args, **kwargs)

Kui __new__()see kutsutakse, edastatakse klass ise esimese argumendina automaatselt ( cls).

Jällegi, nagu mina, on ka cls lihtsalt nimetamise kokkulepe. Lisaks kasutatakse * args ja ** kwargs meelepärase arvu argumentide võtmiseks Pythonis meetodikutsete ajal.

Mõned olulised asjad, mida rakendamisel meeles pidada, __new__()on:

  • __new__()kutsutakse alati enne __init__().
  • Esimene argument on klass ise, mis antakse edasi kaudselt.
  • Tagastage alati kehtiv objekt __new__(). Pole kohustuslik, kuid selle peamine eesmärk on objekti loomine ja tagastamine.

Vaatame ühte näidet:

 class Point(object): def __new__(cls,*args,**kwargs): print("From new") print(cls) print(args) print(kwargs) # create our object and return it obj = super().__new__(cls) return obj def __init__(self,x = 0,y = 0): print("From init") self.x = x self.y = y

Nüüd, kiirustame selle nüüd.

 >>> p2 = Point(3,4) From new (3, 4) () From init

See näide illustreerib seda, mida __new__()on varem kutsutud __init__(). Samuti näeme, et parameeter cls __new__()on klass ise ( Point). Lõpuks luuakse objekt, kutsudes __new__()meetodi objekti baasklassi.

Pythonis objecton baasklass, millest on tuletatud kõik teised klassid. Ülaltoodud näites oleme seda teinud super () abil.

Kas kasutada funktsiooni __new__ või __init__?

Võib-olla olete näinud __init__()väga sageli, kuid selle kasutamist __new__()on harva. Seda seetõttu, et enamasti pole teil vaja seda alistada. Üldiselt __init__()kasutatakse seda äsja loodud objekti initsialiseerimiseks, samal ajal kui __new__()seda kasutatakse objekti loomise viisi juhtimiseks.

Saame kasutada ka __new__()objekti atribuutide initsialiseerimiseks, kuid loogiliselt peaks see olema sees __init__().

Üheks praktiliseks kasutuseks __new__()võiks olla klassist loodud objektide arvu piiramine.

Oletame, et me tahtsime klassi SqPointeksemplaride loomiseks, mis esindaksid ruudu nelja tippu. Saame pärida oma eelmisest klassist Point(selle artikli teine ​​näide) ja kasutada __new__()selle piirangu rakendamiseks. Siin on näide, kuidas piirata klassi ainult nelja eksemplari.

 class SqPoint(Point): MAX_Inst = 4 Inst_created = 0 def __new__(cls,*args,**kwargs): if (cls.Inst_created>= cls.MAX_Inst): raise ValueError("Cannot create more objects") cls.Inst_created += 1 return super().__new__(cls)

Proovijooks:

 >>> p1 = SqPoint(0,0) >>> p2 = SqPoint(1,0) >>> p3 = SqPoint(1,1) >>> p4 = SqPoint(0,1) >>> >>> p5 = SqPoint(2,2) Traceback (most recent call last):… ValueError: Cannot create more objects

Huvitavad Artiklid...