Область видимости локальных переменных
Содержание курса
Ловушка: локальная переменная не меняет глобальную с тем же именем
Есть ситуация хитрее, чем NameError: код выполняется без единой ошибки, но результат не тот, что ожидался. Это происходит, когда внутри функции и снаружи существуют переменные с одним и тем же именем.
Посмотрим на пример:
x = 100
def set_x():
x = 42
print("внутри функции:", x) # 42
set_x()
print("снаружи функции:", x) # 100
Запустите этот код — Python не выдаст никакой ошибки. Но глобальная x так и осталась 100, хотя внутри set_x() мы явно написали x = 42.
Происходит вот что: строка x = 42 внутри функции создаёт новую, отдельную локальную переменную с именем x. Она живёт в пространстве имён функции и никак не связана с глобальной x. Когда set_x() завершается, локальная x исчезает, а глобальная остаётся нетронутой.
Почему это ловушка? Потому что код выглядит «рабочим»: нет ни NameError, ни предупреждений. Если не вывести значения явно, как в примере выше, можно долго не замечать, что функция вообще ничего не сделала с внешней переменной.
Это называют теневым именованием (shadowing): локальное имя «заслоняет» глобальное внутри функции, но не меняет его.
Типичный сценарий, где это приводит к багу:
счётчик = 0
def increment():
счётчик = счётчик + 1 # UnboundLocalError!
Здесь Python видит присваивание счётчик = ... внутри функции и решает, что счётчик — локальная переменная. Но в правой части присваивания эта же локальная счётчик ещё не существует — и вместо ожидаемого «прибавить 1 к глобальному счётчику» получаем UnboundLocalError.
Правильный подход — не пытаться менять глобальную переменную изнутри функции напрямую. Если функции нужно вернуть новое значение — используйте return:
счётчик = 0
def increment(n):
return n + 1
счётчик = increment(счётчик)
print(счётчик) # 1
Функция получает текущее значение через параметр, вычисляет новое и возвращает его. Вызывающий код сам решает, куда сохранить результат. Это и безопаснее, и понятнее: функция не зависит от того, как называется переменная снаружи.
