2.16. Rozsah platnosti¶
Když Python vyhledává název uvnitř funkce, prohledává určitou posloupnost rozsahů platnosti. Pochopení této posloupnosti vysvětluje, proč některá přiřazení zastiňují vnější názvy, proč jiná je modifikují a proč si vnořené funkce mohou pamatovat hodnoty z místa, kde byly definovány.
Vyhledávání názvu začíná v lokálním rozsahu funkce a postupuje směrem ven do modulových a vestavěných rozsahů, dokud není nalezena shoda.¶
2.16.1. Lokální a modulový rozsah¶
Názvy definované uvnitř funkce jsou lokální pro tuto funkci a po skončení volání zmizí:
def f():
x = 10
print(x)
f()
print(x) # NameError: x is not defined
Názvy definované na nejvyšší úrovni souboru .py jsou na úrovni modulu (někdy nazývané globální) a jsou viditelné všude v daném souboru, včetně uvnitř funkcí:
CAMERA = "OpenMV"
def banner():
print("running on", CAMERA)
Čtení názvu na úrovni modulu zevnitř funkce je automatické. Přiřazení k tomuto názvu zevnitř funkce nikoli – přiřazení vytvoří novou lokální proměnnou, která modulovou proměnnou po zbytek volání zastíní:
counter = 0
def bump():
counter = counter + 1 # UnboundLocalError
Levé counter činí z counter lokální název ve funkci bump, takže čtení vpravo nemá žádnou hodnotu, kterou by našlo.
2.16.1.1. Klíčové slovo global¶
Chcete-li skutečně znovu přiřadit název na úrovni modulu zevnitř funkce, nejprve jej deklarujte jako global:
counter = 0
def bump():
global counter
counter += 1
Po global sahejte jen zřídka. Funkce, které mění skrytý stav, se hůře chápou než funkce, které přijímají hodnoty jako argumenty a vracejí nové hodnoty ven. Obvyklým řešením pro „potřebuji sdílet stav“ je předat objekt (seznam, slovník, instanci třídy) jako argument a měnit jej místo toho.
2.16.2. Lambdy¶
lambda vytváří malou anonymní funkci v jediném výrazu:
square = lambda x: x * x
square(7) # 49
Je přesně ekvivalentní
def square(x):
return x * x
Tělo lambda musí být jediný výraz – žádné příkazy, žádné více řádků. Hlavním použitím je předání drobné funkce jako argumentu něčemu, co funkci přijímá:
pairs = [("b", 2), ("a", 3), ("c", 1)]
pairs.sort(key=lambda item: item[1])
# [('c', 1), ('b', 2), ('a', 3)]
Když tělo přeroste jeden výraz, přejděte na skutečné def. Pojmenování funkce pomocí def jí také dá název ve výpisech zásobníku, který lambda nemá.
2.16.3. Uzávěry¶
Funkce definovaná uvnitř jiné funkce může číst názvy z rozsahu obklopující funkce. Vnitřní funkce tyto názvy zachytí a funguje dál i poté, co se vnější volání vrátilo:
def make_adder(n):
def add(x):
return x + n
return add
add5 = make_adder(5)
add10 = make_adder(10)
print(add5(100), add10(100))
Výstup:
105 110
add5 a add10 jsou dvě samostatné funkce, z nichž každá si pamatuje své vlastní n. Funkce, která tímto způsobem vytváří a vrací přizpůsobenou vnitřní funkci, se nazývá uzávěr. To je hlavní důvod, proč jazyk vůbec potřebuje vnořené funkce – způsob, jak do hodnoty funkce zapéct nějaký stav a poté tuto hodnotu předat jako jediný volatelný objekt.
Čtení zachycených názvů probíhá automaticky. Opětovné navázání některého vyžaduje další klíčové slovo. Níže uvedený příklad dělá to, co vypadá správně, a selže:
def make_counter():
count = 0
def tick():
count = count + 1 # UnboundLocalError
return count
return tick
Přiřazení k count uvnitř tick činí count lokální pro tick stejným způsobem, jako by jej učinilo lokální ve funkci nejvyšší úrovně. Klíčové slovo nonlocal říká Pythonu „tento název žije v obklopující funkci, znovu jej navaž tam“:
def make_counter():
count = 0
def tick():
nonlocal count
count += 1
return count
return tick
c = make_counter()
print(c(), c(), c())
Výstup:
1 2 3
nonlocal je pro rozsah obklopující funkce tím, čím je global pro modulový rozsah. Všimněte si, že mutace zachyceného objektu (volání some_list.append(...), some_dict[k] = v) nonlocal nepotřebuje – název není znovu navazován, mění se pouze objekt, na který ukazuje.