Python belülről

Python belülről
1. Bevezetés.
2. Tárgyak. A kezdet.
3. Tárgyak. A farok.

Az előző részben elkezdtük tanulmányozni a Python objektumrendszert: megértettük, hogy pontosan mit lehet objektumnak tekinteni, és hogy az objektumok hogyan végzik munkájukat. Folytassuk a kérdés megvitatását.

Üdvözöllek a Python belsejében található cikkeink harmadik részében (erősen javaslom a második rész olvasását, ha még nem tette meg, különben nem fogsz érteni semmit). Ebben az epizódban egy fontos koncepcióról beszélünk, amelyről még mindig nem derül ki az attribútumokról. Ha valaha írtál valamit Pythonra, akkor használtad őket. Az objektum attribútumai a hozzá kapcsolódó egyéb objektumok, amelyek az operátoron keresztül érhetők el. (pont), például: >>> my_object.attribute_name. Röviden írja le a Python viselkedését az attribútumok elérésekor. Ez a viselkedés attól függ, hogy az attribútum által elérhető objektum típusa (már rájött, hogy ez minden objektumhoz kapcsolódó műveletre vonatkozik?).

Egy típushoz olyan speciális módszereket írhat le, amelyek módosítják a példányok attribútumainak elérését. Ezeket a módszereket itt ismertetjük (ahogy már tudjuk, a szükséges résidőkhöz társulnak, például a fixup_slot_dispatchers függvénnyel, ahol létrehozzák a típust ... elolvassa az előző bejegyzést, ugye?). Ezek a módszerek mindent megteszhetnek, bármit is; Van saját típusok: C leírását vagy Python, írhat olyan módszerek megőrzése és vissza az attribútumok néhány hihetetlen helyen, ha úgy akarja, akkor küldhet és fogadhat attribútumok a rádióban, hogy az ISS vagy akár tárolja azokat a relációs adatbázisban. De egy többé-kevésbé normális körülmények között ezek a módszerek csak írj attribútum formájában kulcs-érték párokat (attribútum / attribútum értéke a neve) semmilyen szótárban tárgy, ha az attribútum be van állítva, és visszatér egy attribútumot a szótárban, ha azt kérik (vagy kivételt dobtunk AttributeError: ha a szótárnak nincs olyan kulcsa, amely megfelel a kért attribútum nevének). Ez annyira egyszerű és gyönyörű, köszönöm a figyelmet, valószínűleg befejeznénk ezt.

Állni. Barátaim, a széklet már csak elkezdte a gyors megközelítést egy forgó szélgenerátor. Kiderül, hogy eltűnnek. Azt javaslom, hogy tanulmányozzam, mi történik a tolmácsban, és kérdezzen meg néhány, idegesítő kérdést, mint általában.

Olvassa el figyelmesen a kódot vagy menjen közvetlenül a szöveges leíráshoz:

Nézzük lefordítani emberi nyelv: a tárgy (a legegyszerűbb beépített típus, ha elfelejtette), mint látjuk, van egy szótár, és minden, amit lehet emelkedett a tulajdonságok, azonos azzal, amit látunk az objektum .__ dict__. Meg kell lepődnünk, hogy az objektumtípus (például o objektum) példányai nem támogatják a további attribútumok meghatározását, és egyáltalán nem rendelkeznek __dict__-val. de támogatja a hozzáférést a rendelkezésre álló attribútumokhoz (próbáld ki a. o osztályt. o .__ hash__ stb., ezek a parancsok visszaküldnek valamit). Ezt követően létrehoztunk egy új C. osztályt, amelyet örököltünk az objektumtól. hozzáadta az A attribútumot, és látta, hogy a C.A és C .__ dict __ ['A'] keresztül hozzáférhető. ahogy az várható volt. Ezután létrehoztuk a C osztály o2-es példányát, és láttuk, hogy az attribútumdefiníció változik __dict__. és fordítva, a __dict__ változó hatással van az attribútumokra. Ezután meglepődtünk, hogy a __dict__ osztály csak olvasható, még akkor is, ha az attribútumdefiníció (C.A2) jól működik. Végül láttuk, hogy a példány __dict__ objektumai és az osztály különböző típusúak - az ismerős és a titokzatos dict_proxy. És ha mindez nem elég, emlékezzetek az előző részből álló puzzlere: ha egy tiszta tárgy örökösei (például o) nincsenek __dict__. és C kiterjeszti az objektumot. nem jelent semmi jelentőset, ezért hirtelen a C (o2) osztályú esetekben jelenik meg __dict__?

Ebben a példában az utolsó sor azt mutatja be, hogyan érheti el a típusattribútumot. Ebben az esetben keresse meg az attribútum sávot. meg kell hívni az Foo osztály (a tp_getattro felé mutató) attribútumainak a hozzáférési funkcióját. Körülbelül ugyanaz történik a attribútumok definiálásakor és törlésénél (a tolmács számára az ún. "Törlés" csak az érték NULL-ra állítja). Remélem, hogy eddig minden világos volt, és közben megvitattuk az attribútumok fellebbezését.

Mielőtt megvizsgálnám az esetek attribútumaihoz való hozzáférést, hadd mondjam el egy kevéssé ismert (de nagyon fontos!) Fogalmat: egy leíró. A leíróknak különleges szerepet kell játszaniuk az esetek attribútumainak elérésében, és tisztázni kell, hogy mi az. Egy objektumot leírónak tekintünk, ha annak egy vagy két nyílása (tp_descr_get és / vagy tp_descr_set) nem nulla értékekkel van kitöltve. Ezek a rések speciális módszerekhez kapcsolódnak. __set__ és __delete__ (például ha meg egy osztály eljárás __get__. akik kapcsolatba tp_descr_get nyílásba., és hozzon létre egy objektumot az ebbe az osztályba, a tárgy egy leíró). Végül az objektumot adatleírónak tekintjük. ha a tp_descr_set slot nem nulla értékű. Amint látni fogjuk, a leírók fontos szerepet játszanak az attribútumok elérésében, és még mindig adok néhány magyarázatot és hivatkozást a szükséges dokumentációra.

Tehát kitaláltuk, hogy milyen leírók vannak, és megértettük, hogyan jön létre a típus attribútumokhoz való hozzáférés. De a legtöbb tárgy nem típus; típusuk nem típus. de valami prózaibb, például int. dict vagy egyéni osztály. Mindannyian univerzális attribútum-hozzáférési függvényekre támaszkodnak, amelyek vagy a típusban vannak meghatározva, vagy a típus szülőjéből örököltek, amikor létrejöttek (ez a téma, a résidők öröksége, amit a "Fejben" tárgyaltunk). Az univerzális attribútum-hozzáférési függvény (PyObject_GenericGetAttr) algoritmusa így néz ki:

  1. Keresse meg a példány szótár típusát és a típus minden szülőjének szótárait. Ha van adatleíró. hívja fel a tp_descr_get funkcióját, és adja vissza az eredményt. Ha valami mást találsz, emlékezz erre csak abban az esetben (például X néven).
  2. Keressen az objektum szótárban, és adja meg az eredményt, ha megtalálta.
  3. Ha semmi sem található az objektum szótárban, akkor ellenőrizze az X-et. Ha telepítve van; ha X leíró, hívja meg a funkciót tp_descr_get és adja vissza az eredményt. Ha X egy közönséges tárgy, akkor küldje vissza.
  4. Végül, ha semmit nem találtunk, dobjuk ki a AttributeError kivételt.

Megjegyzem, hogy Python-ban épp most kaptuk meg az objektumorientált örökség teljes megértését: az attribútumok keresése az objektum típusával kezdődik, majd minden szülőben megértjük, hogy elérjük a C1 osztályú O objektum A attribútumát. amely a C2-től örökölt. amely viszont a C3-ból származik. visszatérhet az A és az O. és a C1. és C2 és C3. amelyet a módszerek bizonyos rendezési sorrendje határoz meg, amelyet jól leírtunk itt. Az attribútumok feloldásának ilyen módja és a résidők öröksége elegendő ahhoz, hogy megmagyarázza Pythonban az örökségi funkciók nagy részét (bár az ördög a szokásos módon részletesen le van fedve).

Új osztályt, objektumot hoztunk létre, és definiáltuk annak attribútumát (o.foo = 'bar'), amelyet a gdb beírva hoztunk létre. dereferenciálta az objektum típusát (C), és megállapította, hogy tp_dictoffset (16), majd ellenőrizte, hogy mi van ezen az eltoláson az objektum C-struktúrájában. Nem meglepő, hogy ott találtunk egy objektumszótárt egy foo billentyűvel. jelezve a bár értékét. Természetesen, ha ellenőrizze a tp_dictoffset típusát, amely nem rendelkezik __dict__-val. például objektumot. akkor ott találunk nullát. Lógó dudor, igen?

Az a tény, hogy hasonló típusú és szótárak szótárak hasonlóak, de végrehajtásuk meglehetősen eltérő, zavarba hozhatják. Még mindig vannak néhány rejtvény. Összefoglaljuk, és meghatározzuk, hogy mi hiányzott: definiáljuk az objektumtól örökölt C üres osztályt. hozzon létre egy o objektumot ehhez az osztályhoz, hozzárendeljen további memóriát a szótármutatóhoz az offset tp_dictoffset-hez (a tér a kezdetektől elkülönítve van, de a szótár csak az első (bármilyen) konverzióhoz van hozzárendelve, itt van a sneer.). Ezután hajtsa végre az o .__ dict__ tolmácsot. A bytecode a LOAD_ATTR paranccsal áll össze. amely a PyObject_GetAttr függvényt hívja. amely dereferenciálja az objektum o típusát és megtalálja a tp_getattro rést. amely a fentiekben leírt és a PyObject_GenericGetAttr-ben végrehajtott szabványos attribútumkeresési folyamatot futtatja. Végül, miután ez megtörténik, mi visszatér a tárgyunk szótárához? Tudjuk, hogy a szótár hol van tárolva, de láthatjuk, hogy __dict__ nem rendelkezik vele, így a csirke- és tojásprobléma felmerül: mi hoz vissza a szótárba, amikor __dict__ -ra fordulunk. ha nincs sem a szótárban?

Az objektumszótár fölött elsőbbséget élvező dolog egy leíró. Lásd:

Itt van! Vidto, hogy van valami, nevezzük getset_descriptor (./Objects/typeobject.c file), egy bizonyos funkciók csoportjának, felismerve a leíró protokoll, és hogy legyen az objektum __dict__ típusát. Ez a kar eltéríti minden hozzáférési kísérletet o .__ dict__ ilyen típusú objektum, és vissza fog térni, amit akar, a mi esetünkben, ez lesz a mutatót a szótárban tp_dictoffset eltolódást o. Ez azt is megmagyarázza, hogy miért láttuk a dict_proxy-ot egy kicsit korábban. Ha a tp_dict-ben van egy mutató egy egyszerű szótárhoz, miért látjuk azt egy olyan objektumba csomagolva, amelyben nem lehet írni valamit? Ez a típustípus típusának __dict__ leírója.

Vége ennek a példanek:

Ez a leíró olyan függvény, amely egy szótárba ágyaz egy egyszerű objektumot, amely szimulálja a hagyományos szótár viselkedését, kivéve, hogy csak olvasható. Miért olyan fontos megakadályozni, hogy a felhasználó beavatkozzon a __dict__ típusba? Mivel egy típus névterének speciális módszerei lehetnek, például __sub__. Amikor speciális típusú módszerekkel létrehozunk egy típust, vagy amikor egy attribútumon keresztül adjuk meg őket, akkor a update_one_slot függvény végrehajtódik. amelyek ezeket a módszereket a típus résszel összekapcsolják, például, ahogy ez a kivonás mûködésével történt az elõzõ posztban. Ha tudnánk hozzá ezek a módszerek közvetlenül a __dict__ típusú, akkor nem jutott volna a kapcsolatot a nyílásokba, és megkaptuk, azt írja, ami hasonló ahhoz, amire szüksége (például azt __sub__ a szótárban), de aki úgy viselkedik, másképp .

Tudod mit? Nem csak lányok. Szeretjük ezeket a srácokat is. Gyere hozzánk. Együtt szórakoztatóbb.

Kapcsolódó cikkek