Hogyan valósítsunk meg írható és csak olvasható adatattribútumokat vegyesen?

A feladat tehát az, hogy egy osztályt úgy definiáljunk, hogy annak példányaiban bizonyos adatattribútumok írhatók és olvashatók, ugyanakkor más adatattribútumok csak olvashatók legyenek. Ez az igény olyan esetekben fordulhat elő, amikor a modellezett entitásnak vannak olyan jellemzői, adatai, amelyek a születése, létrejötte után nem változnak, és vannak olyanok, amelyek változhatnak.

Vegyünk például egy embert, egy személyt. Születése után a személy rendelkezik olyan adatokkal, amelyek élete végéig változatlanok. Ilyen mindenképpen a születési helye és ideje. De általában ilyen a neve és a nemzetisége is (most az egyszerűség kedvéért a névváltoztatás ritka esetétől tekintsünk el). Ezzel együtt van számos más jellemző, ami változik vagy változhat a személy élete során. Ilyenek lehetnek a fizikai jellemzők mint pl. a magasság, súly vagy hajszín.

Ha egy személyt akarunk modellezni egy Person nevű osztállyal, akkor érdemes a felülírhatóság szempontjából megkülönböztetni az attribútumokat, és úgy megalkotni az osztályt, hogy az élethossz során nem változó jellemzők csak olvasható attribútumokként szerepeljenek, a többihez pedig biztosítsuk a módosíthatóságot. Ez utóbbi esetén általában illik a kontrollált módosíthatóságot biztosítani, vagyis azt, hogy az értékadás tényleges megtörténtét megelőzően lehessen érték-, típus- vagy bármi más ellenőrzést végezni.

Mindezt általában tulajdonságok (property) definiálásával szokás megtenni, ahol a nem változtatható tulajdonságok csak getter metódussal, a módosíthatók pedig getter és setter metódusokkal egyaránt rendelkeznek. Ezt láthatjuk alább, ahol a Person osztályban a név, a születési hely és idő, valamint a nemzetiség csak olvasható tulajdonságként szerepel. Az életkorral változó magasság viszont írható tulajdonság, ahol az elfogadható magasságértéket ellenőrizzük.

Ez a megoldás alapvetően megfelel a követelményeknek. Azonban van két nem előnyös vonása. Az egyik, hogy ha minél több a nem változtatható attribútumok száma, annál több getter metódusra van szükség az osztályban, ami a kód áttekinthetőségét csökkenti. A másik, hogy a getter metódusok a háttérben egy privátnak jelölt attribútum értékét adják vissza. Mint tudjuk, Pythonban az, hogy egy attribútum privát csak egy jelölési konvencióval valósul meg, valójában, ha valaki ismeri a privát attribútum nevét, akkor felül tudja írni. Márpedig egy objektum attribútumainak nevei könnyen megtudhatók.

Kérdés, hogy lehet-e olyan megoldást találni, amely e két hátrányt kiküszöböli?

Igen, mégpedig a collections modul namedtuple konténerének segítségével. Ez egy olyan speciális tuple, amelynél az elemek nem csak indexszel, hanem az értékekhez rendelt nevekkel, más szóval mezőnevekkel, a normál attribútumeléréshez használt szintaxis formájában is hozzáférhetők. Mivel tuple-ról van szó, ami változtathatatlan konténer, ezért az értékek csak kiolvashatók, de nem írhatók. E két jellemzőjét, vagyis a csak olvashatóságot, és az attribútumelérési formájában történő értékhozzáférést fogjuk kihasználni. Mégpedig úgy, hogy a Person azon attribútumaiból, amelyeket csak olvashatónak szánunk, egy mezőneves tuple típust hozunk létre, és a Person osztály ebből fog örökölni. A Person többi, módosítható attribútumait írható tulajdonságként definiáljuk. Eddig egyszerűen hangzik, de egy dologra még oda kell figyelni. Mégpedig arra, hogy mivel nem változtatható konténerből öröklünk, a példányok adatattribútumainak létrehozása és inicializálása a szokásos módon, vagyis az __init__-en keresztül, nem működik. Helyette a __new__ definiálása szükséges.

A Person osztály ezen megfontolásokkal kialakított definícióját mutatja a következő programkód.

A Person példányaiban a csak olvasható attribútumok ténylegesen védettek, nem lehet őket semmilyen kerülő módon felülírni. És az is látszik, hogy az osztály definíciója sokkal áttekinthetőbb lett.

E bejegyzés témájához és megértéséhez a Python tudásépítés lépésről lépésre című e-könyv következő részei kapcsolódnak elsősorban: a „Mágikus metódusok egyedi igényre szabott osztályokban” fejezet „A példányosítási folyamat befolyásolása” alfejezete, valamint a „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezet, „Speciális konténer típusok” alfejezetén belül a „Mezőneves tuple – namedtuple” cím.

Érdekel a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyv.