Глобальная и локальная видимость переменных в Python
Глобальные и локальные переменные: основные принципы
В Python область видимости переменной определяет, где в коде эта переменная доступна. Переменные, созданные вне функций, считаются глобальными. Внутри функции переменные по умолчанию локальны, если только не указано иное.
Наиболее эффективный способ изменить глобальную переменную из функции - использовать инструкцию global. Без неё присваивание внутри функции создаст новую локальную переменную, скрывающую глобальную.
x = 10
def change():
global x
x = 20
change()
print(x) # 20Python global local (глобальные и локальные переменные в python)
Пояснение: строка global x объявляет, что x относится к глобальной области видимости. Последующее присваивание меняет глобальную переменную.
Типичная ошибка: попытка изменить глобальную переменную без global вызывает UnboundLocalError.
y = 5
def f():
y = y + 1 # Ошибка: y считается локальной до присваивания
f()Решение: добавить global y внутри функции.
Цель использования: когда необходимо модифицировать глобальное состояние из нескольких функций, например счётчики вызовов или общие настройки.
Как изменить глобальную переменную, не используя global?
Можно передать значение как аргумент и вернуть новое значение. Это более функциональный подход, избегающий побочных эффектов.
x = 10
def change(val):
return val + 10
x = change(x)
print(x) # 20Проблема: если нужно изменить сразу несколько глобальных переменных, такой код становится громоздким. Также теряется прямое влияние на глобальное состояние, что может быть неудобно для простых сценариев.
Когда использовать: когда предпочтительна чистота функций и отсутствие скрытых модификаций, особенно в многопоточных или тестируемых приложениях.
Как работать с глобальными переменными во вложенных функциях?
Для вложенных функций (функция внутри функции) применяется инструкция nonlocal. Она позволяет изменять переменную из внешней, но не глобальной области.
def outer():
x = 10
def inner():
nonlocal x
x = 20
inner()
print(x) # 20
outer()Ошибка: использование nonlocal для переменной, которой нет во внешней функции, вызывает SyntaxError.
Применение: замыкания, декораторы, счётчики вызовов внутри фабрик функций.
Как изменить глобальный изменяемый объект (список, словарь) без global?
Если не переназначать саму переменную, а только изменять её содержимое (методами append, pop, присвоением по индексу), то global не требуется. Объект остаётся глобальным.
my_list = [1, 2, 3]
def add_item():
my_list.append(4)
add_item()
print(my_list) # [1, 2, 3, 4]Ложное ощущение безопасности: если внутри функции выполнить my_list = [5], то создастся локальная переменная, а глобальный список не изменится. Это частая ошибка новичков.
Когда уместно: когда объект уже существует и необходимо только мутировать его, например очередь задач.
Как получить доступ ко всем глобальным переменным программно?
Функция globals() возвращает словарь глобальных переменных. Можно читать и изменять их динамически.
a = 100
def show_globals():
for name, value in globals().items():
if not name.startswith('__'):
print(name, value)
show_globals() # a 100Не рекомендуется для продакшена: злоупотребление globals() усложняет чтение кода и может привести к неожиданным побочным эффектам.
Случаи использования: метапрограммирование, отладка, фреймворки с динамической загрузкой конфигураций.
Расширенные примеры с пояснениями
Пример 1: Изменение глобальной числовой переменной с global
counter = 0
def increment():
global counter
counter += 1
increment()
increment()
print(counter) # 22
Объяснение: global counter позволяет изменять глобальный счётчик. Без этой строки возникла бы ошибка UnboundLocalError.
Пример 2: Использование nonlocal во вложенных функциях (замыкание-счётчик)
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
c1 = make_counter()
print(c1()) # 1
print(c1()) # 2
c2 = make_counter()
print(c2()) # 11 2 1
Объяснение: nonlocal count дает вложенной функции доступ к переменной count из внешней функции make_counter. Каждый вызов make_counter создаёт собственное замыкание.
Пример 3: Изменение элементов глобального списка без global
data = [10, 20, 30]
def update_data():
data[0] = 99
update_data()
print(data) # [99, 20, 30][99, 20, 30]
Объяснение: присваивание по индексу data[0] = 99 не пересоздаёт локальную переменную, а модифицирует существующий глобальный объект.
Пример 4: Создание локальной переменной, скрывающей глобальную (теневая переменная)
x = 'глобальная'
def func():
x = 'локальная'
print(x) # локальная
func()
print(x) # глобальнаялокальная глобальная
Объяснение: внутри функции присваивание x = ... создаёт новую локальную переменную, глобальная не затрагивается.
Пример 5: UnboundLocalError и его исправление
y = 5
def bad():
print(y) # компилятор считает y локальным из-за последующего присваивания
y = 10
bad()UnboundLocalError: local variable 'y' referenced before assignment
Решение:
def good():
global y
print(y)
y = 10
good() # 55
Объяснение: интерпретатор видит присваивание внутри функции и решает, что y локальна. Инструкция global y отменяет это.
Пример 6: Использование globals() для динамического изменения глобальных переменных
config = {'port': 8080}
def set_config(key, value):
if key in globals():
globals()[key] = value
else:
print('Нет такой глобальной переменной')
set_config('config', {'port': 9000})
print(config) # {'port': 9000}{'port': 9000}Объяснение: globals() возвращает словарь, который можно изменять. Однако это снижает читаемость, и лучше использовать явные присваивания.
Пример 7: Передача глобальной переменной как аргумент для избежания global
name = 'Анна'
def greet(n):
return f'Привет, {n}!'
print(greet(name)) # Привет, Анна!Привет, Анна!
Объяснение: функция не изменяет глобальное состояние, а лишь использует переданное значение. Это предпочтительный способ для чистых функций.
Пример 8: Комбинация global и изменяемых объектов (списки)
items = []
def add_item(new_item):
global items
items.append(new_item)
add_item('молоко')
add_item('хлеб')
print(items) # ['молоко', 'хлеб']['молоко', 'хлеб']
Объяснение: global items обязательно, если вы хотите переназначить саму переменную (например, items = []). Но если переназначения не происходит, global не требуется. В данном случае append не переназначает переменную, но global всё равно указан избыточно для наглядности.