Короткий ответ
nonlocal позволяет функции, вложенной в другую функцию, изменять переменную из области видимости объемлющей (не глобальной) функции, тогда как global всегда ссылается на переменную модульного уровня — без nonlocal попытка присвоить значение внешней переменной внутри вложенной функции создаёт новую локальную переменную во вложенной функции.
Как это работает подробнее
Python следует правилу LEGB (Local, Enclosing, Global, Built-in) при поиске имени для чтения, но для присваивания переменной по умолчанию выбирается самая внутренняя (Local) область видимости. nonlocal name явно указывает интерпретатору искать и изменять name в ближайшей объемлющей функции (Enclosing), а не создавать новую локальную переменную во вложенной функции.
Пример кода
example.py
def make_counter():
count = 0
def increment():
nonlocal count # без этого — UnboundLocalError при count += 1
count += 1
return count
return increment
counter = make_counter()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3 — состояние сохраняется между вызовами замыканияЧто использовать на практике
- Практика. Типичный случай — функция-фабрика, создающая счётчик, аккумулятор или кэш внутри замыкания: внутренняя функция должна изменять состояние, "принадлежащее" внешней функции, между вызовами. Это распространённый паттерн до появления (или как альтернатива) классов для простого хранения состояния.
Подводные камни
- Риски. Забывают
nonlocalи получаютUnboundLocalErrorпри попытке изменить переменную объемлющей функции внутри вложенной. Также пытаются использоватьnonlocalдля переменной модульного уровня, что вызываетSyntaxError: no binding for nonlocal found, если подходящей объемлющей функции нет — в этом случае нуженglobal.