Mi a closure és mire használható?

A Pythonban számos alkalmazási formában találkozhatunk closure függvényekkel. Ezen alapulnak például a dekorátorok, amelyek a Python jellegzetes, hasznos, és ezért gyakran használt nyelvi szerkezetei.

Az olyan függvényeket, amelyeket egy másik függvényen belül hoztunk létre és e körülzáró függvény visszatérési értékeként kapunk closure függvénynek nevezzük.  Ennek jellemzője nem csak az, hogy egy másik függvényben van definiálva, hanem az, hogy

1) a körülzáró függvényen kívül is elérhető még akár olyankor is, ha a körülzáró függvény már nem is létezik.

2) mintegy össze van zárva a létrehozó környezetével (innen az elnevezés eredete), ami azt jelenti, hogy a körülzáró függvény belső lokális változóihoz akkor is hozzáfér, ha a vezérlés már nincs a körülzáró függvényen.

Ez olyan, mintha a closure függvénynek emlékezete lenne, vagyis nem számít, hogy a programvezérlés kikerült a körülzáró függvényből, mert a lokális változói értékének emléke megmarad a closure számára, annak minden egyes hívásakor használhatóan, függetlenül attól, hogy hol és hányszor hívjuk meg a programkódban.

Hasonlatos egy kicsit a closure egy katonai bázisban állomásozó katonához, aki bárhol a bázison kívül bevethető, és az akció helyszínére a bázison biztosított eszközöket a hátizsákjában mindig magával tudja vinni.

Nézzünk most egy egyszerű, szemléltető példaalkalmazást is.

A feladat legyen az, hogy egy étterem asztalonkénti fogyasztását kell az egyes rendelések után folyamatosan nyilvántartani. Két menüt kínál az étterem, amiből a vendégek hétköznap és hétvégén egy-egy asztálnál választhatnak. Ha egy adott asztalnál a vendég befejezte és fizetne, akkor meg kell tudni mondani a végösszeget. De, ha menet közben rákérdez az addigi költésre azt is meg kell tudni adni.

A feladatot több módon is meg lehet oldani. Most mi closure függvénnyel tesszük ezt meg. Az alábbiakban láthatjuk a program teljes kódját.

Két szótárban tároljuk a kínált ételeket és azok árát. A fogyasztás nevű függvény argumentuma e szótárak valamelyikét fogadja. E körülzáró függvényen belül egy összegző nevű függvényt definiálunk, amelynek a rendelt és fogyasztott étel nevét kell argumentumként megadni. Az étel, valamint a körülzáró függvény argumentumában megkapott menü alapján meghatározzuk az árat, amit az étel_árak listában tárolunk. Az összegző függvény e lista aktuális összegét adja vissza. A körülzáró függvény pedig az összegző függvényobjektummal tér vissza.

Tegyük fel, hogy két asztalhoz érkeznek vendégek. Ezért a fogyasztás függvényt kétszer meghívjuk az aktuális menüvel. Az így létrejövő asztal1 és asztal2 két closure függvény. A rendelések a szokásos fázisokban történnek (előétel, főétel, desszert). Minden új étel rendelésénél a megfelelő closure függvényt meghívjuk a fogyasztott étellel. A végén pedig kiírjuk a fizetendő összegeket.

A megoldás előnye, hogy átlátható és rugalmas kódot eredményez. Ez utóbb azt jelenti, hogy ha újabb vendég érkezik egy új asztalhoz, akkor nem kell a már meglévő kódokba belenyúlni, hanem csak egyszerűen bővíteni a programot az előzőek mintájára.

E témával ennél sokkal részletesebben foglalkozik a Python tudásépítés lépésről lépésre című e-könyv „Környezetükkel egységbe forrt függvények – closures” című fejezete.

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