Hogyan nyerjük ki egy szótár elemeit (kulcs-érték párjait), az értékek szerint rendezve?

Tegyük fel, hogy egy tornászcsapat tagjainak magasságértékei a nevükhöz rendelve egy szótárban állnak rendelkezésre. A tornászokat csökkenő magasság szerint tornasorba akarjuk állítani, vagyis úgy szeretnénk a szótár elemeit egymás után kinyerni, hogy a kiolvasási sorrend a magasság szerinti csökkenő sorrendet jelentse.

Egy korábbi bejegyzésben már volt szó arról, hogy elviekben a szótár mint konténerobjektum vagy adatszerkezet nem sorrendtartó. De vannak használati esetek, amikor a beviteli sorrend szerinti elemkikérés lehetősége egyszerűsítheti egy adott probléma megoldását. Ehhez kínálja a Python a collections modul OrderedDict típusát, illetve a 3.7 verziótól már a dict típusú szótár is megőrzi az elemek sorrendjét.

Ha ez így van, akkor a feladatunk megoldása abból áll, hogy név-magasság értékeket tartalmazó szótár elemeit valamilyen módon rendezzük és ezekből egy új szótárt hozunk létre, amelyből a tornasor szerint kérhetjük ki az elemeket (név-magasság), de valójában elég csak a neveket, hiszen a tornasorba állításhoz a néven kell szólítani a csapat tagjait.

Az eredeti szótár elemeinek értékek szerinti rendezéséhez legegyszerűbb a sorted() beépített függvényt használni. A függvény egy listát hoz létre, amelynek elemei az első argumentumként átadott iterálható objektum értékeit rendezve tartalmazza alapesetben növekvő sorrendben, de ha az opcionális reverse argumentumot True értékre állítjuk, akkor csökkenő sorrendet kapunk. A függvénynek meg lehet még adni egy további opcionális argumentumot a key nevű paraméterhez rendelve. Ez egy olyan egyparaméteres függvényt fogad, amely a sorrend megállapításához szükséges összehasonlítás előtt az egyes elemekre meg lesz hívva, és a visszatérési értékek lesznek összehasonlítva. Ha a key paramétert nem használjuk, akkor az elemekre definiált alapértelmezett összehasonlítás alapján fog a rendezés megtörténni.

A sorted() függvényről további részleteket (például, hogy mit jelent és miért jó, hogy a sort() stabil rendezést biztosít) a Python tudásépítés lépésről lépésre című e-könyv „Beépített beépített függvények” fejezetének „Iterálható objektumok függvényei” alcíme alatt olvashatunk.

Az alább látható kódsorokban az első megoldásnál az OrderedDict szótárt alkalmaztuk, amely konstruktorát a sorted() függvény eredményével hívtuk meg. Ez akkor működik, ha a sorted() első argumentumaként a szótár elemeinek (kulcs-érték párokat tartalmazó kételemű tuple objektumok) sorozatát, vagyis a szótár items() metódusának eredményét adjuk meg. Ehhez illeszkedve, a key paraméterhez rendelt, a rendezési értéket (jelen esetben a magasságot) meghatározó függvényt egyik esetben egy lambdakifejezéssel, másik esetben az operator modul itemgetter elemkinyerő objektummal definiáltuk. Ennek argumentumaként a kinyerni kívánt elemérték indexét kell megadni csakúgy, mint a lambdakifejezésben. Ez most 1, mivel a név-magasság értékeket tartalmazó kételemű tuple második elemét akarjuk azonosítani.

A sorted() key argumentumaként általában az itemgetter a preferált, mert összetettebb rendezés esetén kevesebb kód (kisebb hibázás) és jobb olvashatóság érhető el. Az itemgetter egyéb részleteiről és alkalmazásairól a Python tudásépítés lépésről lépésre című e-könyv „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezetében a „Műveletek függvények formájában – operator modul” alcím alatt találunk további információkat.

A második megoldás hasonló az elsőhöz azzal az eltéréssel, hogy OrderedDict helyett a dict szerepel, minthogy immár az is sorrendtartó.

A harmadik megoldás logikája ugyanez, de most szótárépítő kifejezést használtunk a rendezett szótár előállításához.

A negyedik megoldásban szintén szótárépítő kifejezést használunk, de most a sort() key paraméteréhez rendelt, a rendezési értéket (jelen esetben a magasságot) meghatározó függvényt a szótár get() metódusaként definiáltuk. Mivel a get() szótárkulcsot vár argumentumként, ezért a sorted() függvényben a rendezni kívánt sorozatként magát a szótárt adtuk meg, mert mint tudjuk a szótár iterálásakor a kulcsok lesznek kikérve.

Ez a megoldás működik, de ha egy statikus típusellenőrző programmal (pl. mypy) megvizsgáltatjuk, akkor figyelmeztető jelzést kapunk. Ennek oka, hogy a key argumentuma egy egyparaméteres függvény kell, hogy legyen. A get() azonban kétparaméteres, mert a második, az opcionálisan megadható default érték. Ha azt szeretnénk, hogy a statikus ellenőrző program ne méltatlankodjon, de a megoldásunk logikája is megmaradjon, akkor a key paraméterhez a szótár __getitem__ egyparaméteres speciális metódusát kell rendelni. E metódus hívódik meg minden esetben, amikor egy sorozat adott indexű, vagy egy szótár adott kulcsú elemét akarjuk kikérni a [i] szintaxisú operátorral. Ezt mutatja az utolsó megoldási változat.

A __getitem__ metódusról bővebben a Python tudásépítés lépésről lépésre című e-könyv „Speciális metódus- és adatattribútumok” és „Mágikus metódusok egyedi igényre szabott osztályokban” című fejezeteiben olvashatunk.

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