Tegyük fel, hogy adott egy konténerobjektum (pl. egy tuple), amelynek elemei között lehetnek újabb konténerek, és amelyek elemei között szintén előfordulhatnak konténerek. A feladat az, hogy előállítsunk egy listát, amelyben az eredeti konténerben található, tetszőlegesen mélyen beágyazott elemeket gyűjtjük össze. Elemeknek tekintünk minden nem konténerobjektumot, valamint a str és bytes típusú objektumokat.
Az angolban ezt az eljárást a flatten szóval illetik, amely lelapítást jelent. Ezt úgy tudjuk szemléletesen elképzelni, hogy ha a konténert egy alapszintnek tekintve minden egyes beágyazott konténer egy újabb szintnek felel meg. Az egyes szinteken pedig egy vagy több elem helyezkedik el. Amit tenni kell, hogy az elemeket egyetlen szinten helyezzük el. Ezt szemléltetni a következő ábra.

A feladatot több módon meg lehet oldani. Most egy viszonylag egyszerű logikájú, rekurzív generátorfüggvényen alapuló megoldást mutatunk be. A megoldás elve, hogy
- Végighaladunk az adott konténer elemein, és megnézzük, hogy az éppen aktuális elem iterálható-e.
- Ha nem, akkor egy keresett értékkel van dolgunk, és ezt vissza is adjuk a hívónak.
- Ha iterálható az aktuális elem, akkor az 1) pont eljárását ismételjük meg, de most már e beágyazott iterálható objektum elemein végighaladva. Ezt kényelmesen úgy tudunk megtenni, hogy egy yield from utasítás után rekurzívan meghívjuk önmagát a generátorfüggvényt, de most argumentumként az aktuális elemet, mint iterálható objektumot adjuk meg.
Mindezt kódban megvalósítva alább láthatjuk.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
from typing import Iterable def get_element(iterobj:Iterable): for element in iterobj: try: # Megvizsgáljuk, hogy iterálható-e az elem. iter(element) # Ha igen, akkor nem történik kivételdobás, így rekurzívan # meghívjuk a függvényt és kinyerjük az elemeket. # Mivel a str és bytes objektumokat visszaadandó elemnek # tekintjük, így azokra - bár iterálhatók - nem alkalmazzuk a rekurziót. if isinstance(element, (str, bytes)): yield element else: yield from get_element(element) # Ha az aktuális elem nem iterálható, akkor vissza adjuk. except: yield element container = (0, [[1,{2}]], '345', ((6, '78'), b'9'), range(10,13)) # TESZT print(list(get_element(container))) # Eredmény: # [0, 1, 2, '345', 6, '78', b'9', 10, 11, 12] |
Bár a kód egyszerűnek látszik, a működés megértéséhez elég sok előismeret kell. De ezek mind megtalálhatók a Python tudásépítés lépésről lépésre című e-könyvben.
Megjegyezzük, hogy a rekurziót mindig kellő körültekintéssel kell alkalmazni, mert túl mély szintű esetén hibajelzéssel leállhat a program. Az esetünkben azonban ettől nem kell tartani, mert a gyakorlatban a beágyazási szintek száma – amely meghatározza a rekurzió mélységét – nem szokott sok lenni.