Geometriai pont modellezésére melyik megvalósítás a legmegfelelőbb: normál osztály, adatosztály, namedtuple vagy NamedTuple?

Ha geometriai feladatokkal van dolgunk, vagy grafikus felhasználói felületet készítünk, szükségünk lehet egy geometriai pontot modellező osztályra, illetve annak példányaira. A kérdés, hogy ezt hogyan valósítsuk meg?

Ha csak így tesszük fel a kérdést, akkor a válasz persze az, hogy „attól függ”. Ahhoz, hogy érdemben lehessen válaszolni, tudni kell, hogy mit várunk el egy pont példánytól. E követelmények legyenek a következők:

– Legyen a pont példány megváltoztathatatlan objektum, vagyis a létrehozás után az x és y koordináták értékét ne lehessen módosítani.

– Lehessen az x és y koordináták érvényes típusát típusutalással, típusannotációval jelezni.

– Lehessen egyenlőségvizsgálatot végezni két pont példány között. Akkor legyenek egyenlőek, ha mind az x, mind az y koordináták egyenlők az összehasonlított példányokban, és a példányok ugyanazon típusúak.

– Lehessen a pont példány halmaznak eleme, szótárnak kulcsa, más szóval legyen hashelhető.

– Legyen iterálható. Az iteráció eredményeképpen először az x, majd az y értékét adja ki.

– Lehessen egyéni igényeknek megfelelően definiált metódusokat meghívni a példányon. (pl. az x vagy y tengelyre vett tükörképpont előállítása, az origó körül adott szöggel elforgatott pont előállítása)

Több megvalósítási lehetőség közül választhatunk, amelyeket akkor is érdemes megvizsgálni, ha a fenti követelménylista nem mindegyik tételét lehet velük teljesíteni, mert a lista elég szigorú elvárásokat támaszt, amiből a gyakorlatban lehet némi engedményeket tenni.

A következőkben vizsgált megvalósítási opciók ezek: normál osztálydefiníció, továbbá dataclass, namedtuple vagy NamedTuple alapú osztálydefiníciók.

Mindegyik esetben két, eltérő nevű osztályt definiálunk az egyenlőségi relációra tett kitétel miatt, hogy annak teljesülését is tudjuk vizsgálni.

Normál osztálydefinícióval való megvalósítást láthatjuk alább.

A tesztsorok mellett feltüntetett eredményekből látható, hogy az összes követelményt tudjuk teljesíteni, viszont elég sok munka árán. A tulajdonságokat a megváltoztathatatlanság miatt kell definiálni. Az egyenlőségvizsgálathoz és a hashelhetőséghez az __eq__ és __hash__ metódusok kellenek. Az __iter__ pedig az iterálhatósághoz.

Adatosztállyal történő megvalósítás így néz ki:

Itt a követelmények majdnem mindegyike automatikusan teljesül, kivéve az iterálhatóságot. Ezért kell definiálni az __iter__ metódust.

A következő kódsorokban láthatók a NamedTuple alapú osztályok, amelyek szerkezete egyszerű és példányai alapból iterálhatók.

Láthatjuk, hogy az egyenlőségvizsgálatra előírt kitétel – vagyis, hogy azonos koordináták esetén az egyenlőségvizsgálat csak akkor adjon True értéket, ha azonos osztály példányait hasonlítjuk össze – nem teljesül. Nem csak azonos szerkezetű más osztály példányával kapunk egyenlőséget egyező x, y koordináták esetén, hanem egy tuple konténerrel való összehasonlításkor is.

Az előbbiek igazak a namedtuple-t használó, alább látható megoldás esetében is, de itt nem teljesül a koordináták érvényes típusának típusutalással történő jelezhetőségére vonatkozó követelmény sem.

Ami az egyéni metódusokat illeti, ezeket a négy megvalósítási változat mindegyikében tudjuk definiálni, ezért ebben a tekintetben nem lesz különbség. Legfeljebb a kivitelezés lehet egy kicsit furcsa, a megszokottól eltérő a namedtuple esetén.

Nos, melyik megvalósítást válasszuk?

Bár a normál osztálydefiníció minden igényt teljesít, de sok munkával jár, ezért érdemes a többi, gyorsabb kivitelezést lehetővé tevő alternatív megoldást elemezni.

Amennyiben a specifikáció azon részéhez ragaszkodunk, hogy azonos koordináták esetén az egyenlőségvizsgálat csak akkor adjon True értéket, ha ugyanazon osztály példányait hasonlítjuk össze, akkor az adatosztállyal történő megvalósítás a megfelelő választás.

Ha az egyenlőségvizsgálatra vonatkozó fenti kitétel nincs, viszont az x és y koordináták érvényes típusának típusutalással történő jelezhetősége követelmény, akkor a NamedTuple alapú megoldás is jó választás.

Ha a típusannotálás lehetősége sem fontos, mert egy pont esetén közismert, hogy a koordináták értéke csak valós szám lehet, akkor a namedtuple alapú osztálydefiniálás lesz a megfelelő az egyszerűsége okán, különösen, ha egyéni metódusokat sem kívánunk definiálni.

E bejegyzés témája viszonylag széleskörű ismeretet igényel. A Python tudásépítés lépésről lépésre című e-könyvben mindezekről a következő fejezetekben találjuk a szükséges tudnivalókat: „Mágikus metódusok egyedi igényre szabott osztályokban”, „Attribútumműveletek kontrollált végrehajtása”, „Különleges osztálydefiníciók”, valamint a „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezeten belül a „Speciális konténer típusok” és a „Típusutalások és statikus típusellenőrzés támogatása” alfejezetek.

É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.