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.