LEGB rule — это правило поиска пространства имён (scoping) в Python. Аббревиатура расшифровывается как:
len, list, и пр).Когда Python встречает переменную, он ищет сперва локально, потом во вложенных функциях, затем в глобальной области модуля, и, наконец, в пространстве имён встроенных объектов:
def outer(): x = 'enclosing' def inner(): x = 'local' print(x) inner() outer() # 'local'
Если внутри inner() убрать присваивание x, Python возьмёт x из enclosing scope (outer). Если и там переменной нет — ищет глобально, потом из встроенных.
Как изменится поведение функции, если вы забудете объявить
nonlocalилиglobalпри модификации переменной внешнего уровня?
Ответ:
Если попытаться изменить переменную внешнего scope-а внутри вложенной функции без объявления nonlocal (или global — если переменная в модуле), Python создаст новую локальную переменную, не изменив внешнюю.
Пример:
x = 10 def foo(): x = 20 # Это новая локальная x, глобальная не меняется foo() print(x) # 10
Чтобы изменить глобальную x:
def foo(): global x x = 20
С переменными во внешней функции — используйте nonlocal.
История 1
В образовательном проекте использовали вложенные функции для подсчёта числа вызовов. Разработчик писал:
def counter(): count = 0 def inc(): count += 1 return count return inc
Но возникал UnboundLocalError, потому что count внутри inc() считалось новым (создавался локальный count). Решение: объявить count nonlocal.
История 2
В web-приложении хотели в обработчике модифицировать глобально заданный кеш:
cache = {} def add_to_cache(key, value): cache[key] = value
Однако если бы внутри функции попытались полностью переписать cache, например cache = {key: value}, это создало бы новый локальный cache, не связанный с глобальным, из-за отсутствия global cache.
История 3
В большой ETL-системе встретился баг: программисты оставили переменную result без явного инициализации внутри функции, ожидая, что она возьмётся из внешнего scope. Но через несколько итераций результат начал сбиваться, т.к. внутри была запись result += value, а не result = result + value (без nonlocal/global) — и result инициализировалась заново на каждом вызове.