Programarea metaclaselor în Python

Prezentare generală a programării orientate pe obiecte

Să începem cu o scurtă trecere în revistă a ceea ce constituie OOP. În limbile de programare orientate pe obiecte, puteți defini clase. care conectează date și modele de comportament corelate. Clasele pot moșteni unele sau toate caracteristicile clasei părinte, dar ele pot defini, de asemenea, propriile lor atribute (date) și metode (comportamente). În cele din urmă, clasele servesc, de obicei, ca șabloane pentru crearea de instanțe (uneori numite pur și simplu obiecte). Ca o regulă, în diferite cazuri ale unei clase conține o varietate de date, dar toate sunt în aceeași formă - de exemplu, ambele obiecte bob de angajați a angajaților și jane au salarii .salary și camera .room_number. dar valoarea salariilor și numărul de cameră au diferite.







Alte articole din această serie

Unele limbi OOP, inclusiv Python, asigură crearea de obiecte introspective (numite și obiecte reflexive). Obiectele introspective sunt obiecte care se pot descrie: la ce clasă aparțin acest exemplu? Care sunt elementele părinte ale acestei clase? Ce metode și atribute sunt disponibile pentru acest obiect? Introspecția permite funcțiilor sau metodelor care procesează un obiect să ia o decizie bazată pe ce fel de obiect este procesat. Chiar și fără introspecție, funcțiile sunt adesea ramificate pe baza datelor de instanță - de exemplu, calea spre jane.room_number este diferită de calea către numărul bob.room_number. deoarece sunt în camere diferite. Cu ajutorul introspecției, puteți calcula în siguranță mărimea primei pentru jane. în timp ce nu faci calculul pentru bob. de exemplu, deoarece jane are un atribut .profit_share. sau pentru că bob-ul este o instanță a subclaselor oră (angajat).

Răspunsul metaprogramării

Sistemul POP de bază, descris pe scurt, este foarte puternic. Cu toate acestea, în descrierea de mai sus, mai este ceva vizibil: clasele din Python (și în alte limbi) sunt ele însele obiecte care pot fi transferate și introspecte. Deoarece obiectele, după cum sa menționat mai sus, sunt create folosind clase care sunt folosite ca șabloane, ce șabloane sunt folosite pentru a crea clase? Răspunsul este evident - acestea sunt metaclase.

Metaclasele din Python au fost întotdeauna. Cu toate acestea, mecanismul de implementare a metaclaselor de la versiunea Python 2.2 a devenit mult mai accesibil. În special, de la versiunea 2.2, Python a încetat să mai fie o limbă cu o singură metaclauză specială (cea mai mare parte ascunsă), pe baza căreia este creat orice obiect al clasei. Acum, programatorul poate crea subclase de tip metaclaș rădăcină și chiar poate genera dinamic clase utilizând metaclase diferite. Desigur, faptul că puteți manipula metaclase în Python 2.2 nu explică, în sine, de ce ați putea avea nevoie de acest lucru.

Mai mult, nu aveți nevoie de propriile metaclase pentru a vă crea propriile clase. Un concept ușor mai ușor de înțeles este clasa fabrică. O funcție normală poate reveni la o clasă creată dinamic în corpul funcției. În sintaxa tradițională Python, puteți scrie:

Funcția fabrică class_with_method () creează dinamic și returnează o clasă care conține metodele și funcțiile transmise fabricii. Înainte ca clasa să fie returnată, funcția efectuează unele operații cu ea. În modul nou, este prezentată o ortografie mai scurtă, dar fără posibilitatea de a avea cod propriu în corpul fabricii de clase, de exemplu:

În toate aceste cazuri (Foo.Foo2), comportamentul clasei nu este scris direct în cod, ci este creat prin apelarea funcției în timp real, cu argumente dinamice. Trebuie subliniat faptul că nu creăm dinamic exemple de clase. ci și cursurile în sine.

Metaclasses: O soluție care caută o problemă?

Metaclasses-ul este o chestiune foarte profundă, despre care 99% dintre utilizatori nici măcar nu trebuie să gândească. Dacă nu înțeleg de ce ai nevoie de ea - astfel încât să nu aveți nevoie (de oameni au nevoie de fapt, știu exact ce au nevoie de ele, și nu au nevoie pentru a explica - de ce). - Tim Peters (Tim Peters), guru-ul lui Python

Metodele (clase), ca și funcțiile obișnuite, pot readuce obiecte. Prin urmare, într-un sens, este evident că fabricile de clasă pot fi la fel de ușor ca și clase, deoarece acestea pot fi funcții. În particular, Python 2.2+ are o clasă de tip special. care este doar o astfel de fabrica de clase. Desigur, cititorii își vor aminti tipul de funcție mai puțin ambițios (). construit în versiunile vechi ale Python - din fericire, comportamentul funcției tip () vechi este stocat de clasa de tip (cu alte cuvinte, tipul (obj) returnează tipul / clasa obj). Noul tip de clasă funcționează ca o fabrică de clase, la fel ca și funcția new.classobj.







Cu toate acestea, deoarece tipul este acum o clasă (meta), putem să ne creăm liber subclasa sa:

Metodele magice .__ new __ () și .__ init __ () au o natură specială, dar conceptual ele sunt la fel ca orice altă clasă. Metoda .__ init __ () vă permite să configurați obiectul creat; metoda .__ new __ () vă permite să personalizați locația destinației de plasare. Ultimul, desigur, nu este folosit atât de des, dar este posibil ca orice clasă să fie executată în noul stil Python 2.2 (de obicei moștenit, dar nu înlocuit).

Tipul descendenților are o caracteristică care necesită o atenție deosebită; pe ea se poticnește toți cei care lucrează prima dată cu metaclasele. Primul argument pentru aceste metode este de obicei numit cls. nu de sine. deoarece metodele funcționează cu clasa creată, și nu cu metaclasa. De fapt, nu este nimic special aici; toate metodele sunt asociate cu instanțele lor, iar o instanță a unei metaclase este o clasă. Situația este puțin clarificată:

Toate acestea sunt surprinzător de mecanicii obișnuite, însoțite de o anumită comoditate sintactic, care, pe de o parte, face mai ușor să lucreze cu metaclasses, iar pe de altă parte - sunt confuze utilizatori noi. Această sintaxă suplimentară include mai multe elemente. Cu toate acestea, ordinea de a rezolva aceste schimbări este foarte complicată. Clasele pot moșteni metaclasses de la părinții lor - act de faptul că acest lucru nu este același lucru ca având metaclasses ca părinți (o altă greșeală comună). Pentru clasele de stil vechi sarcină variabilă la nivel mondial ar putea duce _metaclass_ de a utiliza un metaclass personalizat. Cu toate acestea, în cele mai multe cazuri, și este abordarea cea mai sigură pentru clasa pe care doriți să creați prin propria sa _metaclass_ metaclass specificat clasa atribut. Această variabilă trebuie să fie stabilite direct în definiția de clasă, deoarece metaclass nu este utilizat în cazul în care atributul va fi instalat mai târziu (după obiectul de clasă trebuie să fie creat). De exemplu:

Rezolvarea problemelor cu magia

Deci, am luat în considerare ideile de bază ale metaclaselor. Cu toate acestea, utilizarea metaclaselor în munca reală este o chestiune delicată. Problema cu utilizarea metaclaselor este că, în arhitectura obișnuită OOP, clasele nu fac prea mult. Structura de moștenire a claselor este utilă pentru încapsularea și împachetarea datelor și a metodelor, dar utilizatorul, de regulă, lucrează numai cu instanțe specifice.

După cum v-ați aștepta, această aplicație afișează o descriere destul de generală a obiectului de date (un obiect instanță obișnuit). Cu toate acestea, dacă această aplicație este transmisă în timp real, putem obține un rezultat complet diferit:

Acest exemplu folosește serializarea gnosis.xml.pickle. dar cele mai moderne pachete gnosis.magic conțin, de asemenea, serializatoare MetaYamlDump MetaClass. MetaPyPickler și MetaPrettyPrint. În plus, utilizatorul "dump.py" pentru aplicație poate solicita utilizarea oricărui "MetaPickler" din orice pachet Python, acolo unde este furnizat. Textul metaclașului care corespunde acestui obiectiv va arăta astfel:

Una dintre cele mai remarcabile realizări ale acestui mecanism este că programator scris o cerere nu știe neapărat ce serializare vor fi utilizate, precum și dacă ar fi adăugat la serializarea sau alte eco-tehnologie pe linia de comandă.

Poate că utilizarea cea mai obișnuită a metaclaselor este similară cu utilizarea meta-claselor: adăugarea, eliminarea și înlocuirea metodelor definite în clasa creată. În exemplul nostru, metoda "originală" Data.dump () este înlocuită de o altă metodă, creată în afara aplicației, la momentul creării clasei Date (și, prin urmare, în fiecare instanță ulterioară).

Alte modalități de a rezolva problemele cu magia

Dacă încercați să creați o instanță a clasei de disertație fără elementele componente imbricate necesare, va fi returnată o eroare descriptivă; Același lucru este valabil pentru fiecare element imbricat. Elementele imbricate corespunzătoare vor fi create din argumente mai simple, atunci când există o singură cale de a "ridica" fără echivoc argumentele la tipul corect.

Chiar dacă clasele de testare sunt adesea (informal) bazate pe DTD existente, instanțele acestor clase se prezintă ca fragmente neformate ale unui document XML, de exemplu:

Valoarea gnosis.xml.validity nu cunoaște nimic despre subseturile DTD și interne. În general, aceste concepte și capabilități sunt reprezentate de metaclasa DTDGenerator. fără nicio modificare a gnosis.xml.validity și simple_diss.py. DTDGenerator nu substituie propria metoda .__ str __ () în clase le-a creat - puteți imprima în continuare un fragment informă de XML - dar acest lucru metaclass vă permite să modificați cu ușurință astfel de metode magice.

Instrumente Metaclass

Pachetul gnosis.magic conține mai multe utilitare pentru lucrul cu metaclase, precum și câteva exemple de metaclase care pot fi utilizate în programarea orientată spre aspect. Cea mai importantă dintre aceste utilitare este import_with_metaclass (). Această funcție, utilizată în exemplul de mai sus, vă permite să importați un modul terță parte, dar să creați toate clasele acestui modul folosind propriul metaclass, nu tip. Puteți defini în metaclase create de dvs. (sau, în general, preluate din altă parte) orice funcție nouă pe care doriți să o introduceți în modulul terță parte. gnosis.magic conține mai multe metaclase serializabile; În alte pachete, există instrumente de urmărire, stocare de obiecte, erori de înregistrare și așa mai departe.

Funcția import_with_metclass () ilustrează o serie de avantaje ale programării folosind metaclase:

Una dintre caracteristicile acestei funcții este aceea că se creează o clasă Meta obișnuită utilizând metaclașul specificat. Cu toate acestea, după adăugarea Meta ca părinte, copiii săi sunt, de asemenea, creați utilizând o metaclase specială. În esență, un creator de meta-clasă și un set de metode moștenite pot purta clase de tip Meta - aceste două părți ale patrimoniului său sunt independente una de cealaltă.

Descărcați resurse

Subiecte conexe







Trimiteți-le prietenilor: