Egyéni, a defaultdict-hez hasonló szótár a hiányzó kulcstól függő alapértékelőállítási lehetőséggel

Tegyük fel, hogy egy cég egy új termékkel jelent meg a piacon. Az előzetes marketing kampány során számos érdeklődő nevét és email címét gyűjtötték be. A potenciális vásárlóknak egy termékmintát szeretnének küldeni az akciós ajánlatuk keretében. Ehhez azonban nem elég az email címek ismerete, hanem a postai kézbesítési címük is kellene. Van ugyan egy nyilvántartásuk meglévő ügyfeleik postacíméről, de ebben nem minden olyan vásárló szerepel, akik most az új termék iránt is érdeklődnek. Mivel nincs már idő a kézbesítési címek begyűjtésére, azért az a döntés született, hogy azon érdeklődők esetén, akik szerepelnek a meglévő postacím nyilvántartásban azoknak erre a címre küldik az ajánlatot a termékmintával. Akik pedig nem szerepelnek ebben az adatbázisban, azoknál az állami lakcímnyilvántartásból kérik le a lakcímüket, és oda postázzák az ajánlatot.

A feladat, hogy egy adott névhez meghatározzuk a kézbesítési címet.

Modellezzük le a cég postacímnyilvántartását és az állami lakcímnyilvántartást egy-egy dict típusú szótárral, ahol a kulcsok az ügyfelek/vevők neve, a hozzá társított érték pedig a kézbesítési cím, illetve a lakcím.

A feladat megoldása nem nehéz, mert csak annyit kell tenni, hogy a céges postacímeket tartalmazó szótár get() vagy setdefault() metódusát meghívjuk egy adott névvel, és default értéknek a központi lakcímnyilvántartásnak megfelelő szótár adott névhez tartozó értékét adjuk meg.

Azt, hogy a get() és setdefault() közül melyiket használjuk attól függ, hogy a céges postacímeket tartalmazó szótár tartalmát bővíteni akarjuk-e az új, potenciális jelöltekkel úgy, hogy kézbesítési címként a lakcímük szerepel. Mindkét esetre megírjuk a programkódokat, amelyek alább láthatók.

Az eredményekből látható, hogy ami a konkrét feladatot illeti, az mindkét esetben egyformán teljesül. Az eltérés, hogy setdefault() esetén a céges nyilvántartás bővült.

Ez a két megoldás, bár a feladat követelményét teljesíti, futási idő szempontjából nem optimális. Ugyanis a default értékek (a lakcímek) mindkét esetben akkor is le lesznek kérdezve és előállítva, ha egyébként arra nem lenne szükség, mert a kulcs (a vevő neve) már szerepel a szótárban (a céges postacím nyilvántartásban). Ha a lekérdezések ideje érzékelhetően hosszú, akkor minél nagyobb a postacímnyilvántartás, azaz minél több címet akarunk megkapni, a teljes idő annál hosszabb lesz.

Ezen úgy lehetne segíteni, ha a default értékek előállítása csak akkor történne, ha a kulcs nincs a szótárban. Erre szolgálna a collections modul defaultdict típusú szótára. Azonban az most nem alkalmazható, mert annak default_factory attribútuma, amely a default érték előállítását végzi, csak egy olyan hívható objektum lehet, amely nem fogad argumentumot. Márpedig esetünkben a default érték (lakcím) függ a kulcstól (ügyfélnév).

Ezért egy egyéni, a defaultdict-hez hasonló szótárszerű objektum osztályát fogjuk definiálni, amely a dict típust örökli, továbbá, amelynek példányosításakor – a defaultdict-hez hasonlóan – első argumentumként egy, a kulcsot fogadni képes egyparaméteres default_factory nevű hívható objektum adható meg, és végül a __missing__ speciális metódust úgy írja felül, hogy abban a default érték előállítása a default_factory meghívásával történik átadva a hiányzó kulcsot. /A dict típusú szótár __missing__ speciális metódusa akkor kerül automatikusan meghívásra, ha kulccsal történő lekérdezés esetén a kulcs nem szerepel a szótárban./

Ennek a CustomDefaultDict nevű osztálynak a definíciója és az ezt használó kódsorokat és eredményeket láthatjuk alább.

Látható, hogy a feladat szempontjából ugyanazt az eredményt kaptuk, mint a setdefault() esetében (a céges nyilvántartás is bővült), de a futási idő most kedvezőbb lesz, mert a lakcímek csak akkor lesznek lekérve, ha a céges nyilvántartásban az adott nevű ügyfél még nem szerepel.

Megjegyezzük, hogy bár a konkrét feladat szempontjából nem releváns, de a CustomDefaultDict úgy lett kialakítva, hogy nemhívható objektumot is fogadhat a default_factory. Ezzel, a get() és setdefault()-hoz hasonlóan, egy meghatározott objektumot tud biztosítani default értékként, ellentétben a defaultdict-tel, ahol ehhez egy konstans értéket visszaadó paraméternélküli függvényt kell átadni.

A Python tudásépítés lépésről lépésre című e-könyvben a dict típusú szótár nyilvános metódusainak ismertetése a „Beépített típusok nyilvános metódusai” fejezetben található, a defaultdict típussal pedig a „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezet „Speciális konténer típusok” alfejezete foglalkozik.

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