Локальные переменные в Python: область видимости
Автор: Казачкин Даниил Михайлович · Обновлено
Имя, которому присвоено значение внутри функции, обычно локально для этого вызова. Оно создаётся при входе в функцию и перестаёт быть доступным снаружи после завершения. Параметры тоже локальны. Это защищает расчёты разных функций от случайного вмешательства.
Одинаковые имена в разных областях
rate = 10
def add_bonus(score):
bonus = 5
result = score + bonus
return result
print(add_bonus(rate))
print(rate)Результат:
15
10Локальные bonus и result снаружи недоступны, а глобальная rate не изменилась. Аргумент 10 был связан с локальным параметром score.
Чтение и присваивание — разные случаи
Функция может прочитать глобальное имя, если локального нет. Но присваивание делает имя локальным, и попытка прочитать его раньше вызовет UnboundLocalError. Ключевое слово global существует, однако для учебных расчётов лучше передавать данные параметрами и возвращать результат: зависимости становятся явными.
Типичная ошибка — вызвать print(result) после функции. Возвращаемое значение нужно сохранить: result = add_bonus(rate). Ещё одна ошибка — считать, что локальная переменная сохраняет значение между вызовами. Каждый вызов создаёт новую область.
Два независимых вызова
Создайте функцию final_price(price, discount), внутри вычислите local variable amount и верните итог. Вызовите её с двумя наборами данных и докажите, что результаты не смешиваются. Затем попробуйте обратиться к amount снаружи и прочитайте NameError. Исправьте код сохранением возвращённого значения, не добавляя global.
Параметры вместо global
Создайте функцию apply_tax(price, rate) и дважды вызовите её с разными ставками. Затем напишите ошибочный вариант, читающий global rate, измените глобальное значение между вызовами и объясните скрытую зависимость. Верните параметр и добавьте значение по умолчанию только в том случае, если оно действительно является правилом задачи. Проверьте, что локальное имя rate не меняет одноимённую переменную снаружи. В конце нарисуйте области имён одного вызова: параметры, локальный result и глобальное имя функции. Такая схема делает NameError и UnboundLocalError предсказуемыми.
Практикум: локальное состояние счётчика
Создайте глобальную переменную score = 10 и функцию с локальной переменной того же имени. Выведите значения внутри и после вызова, заранее предсказав результат. Затем попробуйте увеличить внешнюю переменную без global и разберите UnboundLocalError. Исправьте дизайн, передавая значение параметром и возвращая новое. Вызовите функцию с двумя независимыми баллами и убедитесь, что скрытое общее состояние отсутствует. Это делает тесты воспроизводимыми и порядок вызовов не влияет на ответ.
Контрольная точка
Как Python решает, что имя внутри функции локальное? Свяжите ответ с наличием присваивания и объясните, почему чтение до этого присваивания не обращается автоматически к внешнему значению.
Частые вопросы
Нужно ли избегать всех глобальных переменных?
Константы конфигурации могут быть глобальными и понятными. Изменяемое состояние лучше передавать явно, особенно в учебных функциях: так зависимости видны, а тесты не зависят от предыдущих действий.