
Внутри функций Python мы можем использовать значения глобальных переменных, т.е. определенных вне любых функций, на уровне модуля:
def foo(): print('x is', x) x = 5 foo() # напечает x is 5
Однако если в функции есть присваиваниие x после использования переменной x, то возникнет ошибка:
def foo(): print('x is', x) x = 10 x = 5 foo() # UnboundLocalError: local variable 'x' referenced before assignment
Обатите внимание, что присваивание бывает в следующих ситуациях:
• x = …
• x += …, x -= …
и т.п.
• for x in …
:
• with … as x
:
Чтобы избежать ошибки, мы должны явно указать перед использованием x, что она относится к глобальной области видимости:
def foo(): global x # <-- тут print('x is', x) x = 10 print('x is now', x) x = 5 foo() # ошибок не будет
Подобная проблема возникает и для вложенных функций, когда во внутренней функции мы хотим поймать в замыкание переменную из внушней функции, чтобы далее присвоить ей другое значение. Вот пример – функция, создающее увеличивающийся на 1 счечтик:
def make_inc(): # внешняя ф-ция total = 0 # счетчик def helper(): # внутр. ф-ция total += 1 # тут присваивание переменной return total return helper f = make_inc() print(f()) # UnboundLocalError: local variable 'total' referenced before assignment
Тут нужно другое ключевое слово – nonlocal, которое укажет, что нужно искать переменную во внешней области видимости. Такой пример будет работать, как задумано:
def make_inc(): total = 0 def helper(): nonlocal total # <- тут total += 1 return total return helper f = make_inc() print(f())
Почему мы редко видим global
и nonlocal
?
nonlocal
– специфичная вещь, обычно вместо нее создают класс.
global
потакает плохим практикам программирования. Менять глобальные переменные внутри функций – плохая практика.
📎 Пример.
def foo(): global x print('x is', x) for x in range(2): ... x = 5 foo() # x is 5 foo() # x is 1 (испортили из-за for)
Нет ошибок выполнения, но есть логическая ошибка! После первого вызова foo()
мы испортили глобальную переменную x
, она стала 1 (последним значением в цикле). Надо было просто называть переменные разными именами, и global
не понадобится!
Специально для канала @pyway. Подписывайтесь за новыми статьями. Будет интересно!