Bevezetés a zárásokba Pythonban
Annak megértése, hogy mit, mikor és miért kell használni a zárakat!
A lezárások elegáns Python-konstrukciók. Ebben a cikkben megtudjuk, hogyan kell meghatározni a lezárást, miért és mikor kell használni őket.
Mielőtt azonban rátérnénk arra, hogy mi a lezárás, először meg kell értenünk, mi az a beágyazott függvény, és hogyan működnek a hatókör-szabályok számukra. Tehát kezdjük.
Hatásköri szabályok és beágyazott függvények a Pythonban
Amikor egy függvény végrehajtódik, egy új helyi névtér jön létre, amely azt a helyi környezetet képviseli, amely a függvénytörzsben hozzárendelt függvényparaméterek és változók neveit tartalmazza. A névteret szótárnak tekinthetjük, amelyben a kulcsok az objektumok nevei, az értékek pedig maguk az objektumok.
A nevek feloldásakor az értelmező először a helyi névtérben keres. Ha nincs egyezés, akkor a globális névtérben keres, amely az a modul, amelyben a függvény definiálva van. Ha továbbra sem talál egyezést, akkor végül ellenőrzi a beépített névteret, mielőtt felhozná a NameError kivételt. Ezt az alábbi ábra szemlélteti:
Tekintsük az alábbi példát:
age = 27 def birthday(): age = 28 birthday() print(age) # age will still be 27 >> 27
Amikor változókat rendelünk hozzá egy függvényen belül, mindig a függvény helyi névteréhez vannak kötve; ennek eredményeként a függvénytörzsben lévő age
változó egy teljesen új, 28-as értéket tartalmazó objektumra vonatkozik, nem a külső változóra. Ez a viselkedés a global
utasítással módosítható. Az alábbi példa kiemeli, hogy:
age = 27 name = "Sarah" def birthday(): global age # 'age' is in global namespace age = 28 name = "Roark" birthday() # age is now 28. name will still be "Sarah".
A Python támogatja a beágyazott függvénydefiníciókat is (függvényen belüli függvény). Íme egy példa:
def countdown(start):# This is the outer enclosing function
def display():# This is the nested function
n = start while n > 0: n-=1 print('T-minus %d' % n) display()# We execute the function countdown(3) >>> T-minus 3 T-minus 2 T-minus 1
Zárási függvény meghatározása
A fenti példában mi történne, ha a countdown()
függvény utolsó sora a display
függvényt adja vissza, ahelyett, hogy meghívná? Ez azt jelenti, hogy a függvényt a következőképpen határozták meg:
def countdown(start):# This is the outer enclosing function
def display():# This is the nested function
n = start while n > 0: n-=1 print('T-minus %d' % n) return display# Now let's try calling this function. counter1 = countdown(2) counter1() >>> T-minus 2 T-minus 1
A countdown()
függvény 2
értékkel lett meghívva, a visszaadott függvény pedig a counter1
névhez volt kötve. a counter1()
végrehajtásakor a start
értékét használja, amelyet eredetileg a countdown()
kapott. Így a counter1()
hívásakor az érték még mindig emlékezett, bár már befejeztük a countdown()
függvény végrehajtását.
Ezt a technikát, amellyel bizonyos adatokat (ebben az esetben 2
) csatolnak a kódhoz, zárásnak nevezik a Pythonban.
Ezt az értéket a befoglaló hatókörben akkor is megjegyzi, ha a változó kikerül a hatókörből, vagy magát a függvényt eltávolítják az aktuális névtérből. Megpróbálhatjuk a következő kódot ennek megerősítésére:
>>> del countdown
>>> counter1() T-minus 2 T-minus 1
>>> countdown(2) Traceback (most recent call last): ... NameError: name 'countdown' is not defined
Mikor használjunk zárakat?
Ha kevés metódust (a legtöbb esetben egy módszert) kell megvalósítani egy osztályban, a lezárások alternatív és elegánsabb megoldást jelenthetnek. Ezenkívül a bezárások és a beágyazott függvények különösen hasznosak, ha a lusta vagy késleltetett kiértékelés koncepciója alapján szeretnénk kódot írni. Íme egy példa:
from urllib.request import urlopen def page(url): def get(): return urlopen(url).read() return get
A fenti példában a page()
függvény valójában nem végez semmilyen számítást. Ehelyett csupán létrehoz és visszaad egy get()
függvényt, amely lekéri a weboldal tartalmát, amikor meghívják. Így a get()
-ben végrehajtott számítás ténylegesen késik a program valamely későbbi pontjáig, amikor a get()
értéke megtörténik. Például:
>>> url1 = page("http://www.google.com") >>> url2 = page("http://www.bing.com") >>> url1 <function page.<locals>.get at 0x10a6054d0> >>> url2 <function page.<locals>.get at 0x10a6055f0> >>> gdata = url1() # Fetches http://www.google.com >>> bdata = url2() # Fetches http://www.bing.com >>>
A zárófüggvénybe zárt értékeket meg lehet találni.
Minden függvényobjektumnak van egy __closure__
attribútuma, amely egy sor cellaobjektumot ad vissza, ha ez egy zárófüggvény. A fenti példára hivatkozva tudjuk, hogy a url1
és url2
zárófüggvények.
>>> page.__closure__ # Returns None since not a closure >>> url1.__closure__ (<cell at 0x10a5f1250: str object at 0x10a5f3120>,)
A cellaobjektum cell_contents attribútuma a zárt értéket tárolja.
>>> url1.__closure__[0].cell_contents 'http://www.google.com' >>> url2.__closure__[0].cell_contents 'http://www.bing.com'
Következtetések:
A Python bezárása akkor határozható meg, ha egy beágyazott függvény egy értékre hivatkozik a befoglaló hatókörében. A lezárások az adatok elrejtésének valamilyen formáját biztosítják. A lezárás rendkívül hatékony módja lehet az állapot megőrzésének egy sor függvényhívás során. Bezárási függvény létrehozása Pythonban:
- Kell egy beágyazott függvényünk.
- A beágyazott függvénynek a befoglaló függvényben meghatározott értékre kell hivatkoznia.
- A befoglaló függvénynek vissza kell adnia a beágyazott függvényt.