← Все вопросы

Что делает global и как работают глобальные и локальные переменные?

Задан вчера357 просмотров2 ответа
5

Не понимаю, почему функция не меняет переменную снаружи:

counter = 0
def inc():
    counter = counter + 1   # UnboundLocalError
inc()

Где-то советуют написать global counter. А ещё в проекте хотят «глобальную переменную, видимую из всех модулей» — как это сделать правильно?

2 ответа

8
✓ Принятый ответ — помог автору

Ошибка из-за правила, по которому Python ищет имена — LEGB: Local → Enclosing → Global → Built-in. Сначала локальная область функции, потом объемлющая, потом модульная (глобальная), потом встроенные имена.

Ключевой момент: если внутри функции есть присваивание имени, Python на этапе компиляции помечает имя как локальное для всей функции. В твоём counter = counter + 1 слева присваивание, значит counter локальная. А справа её читают ДО того, как присвоили — отсюда UnboundLocalError.

global говорит «работай с переменной модуля, не создавай локальную»:

counter = 0
def inc():
    global counter
    counter += 1
inc()
print(counter)  # 1

nonlocal — то же, но для объемлющей функции (замыкания), не для модуля:

def make_counter():
    n = 0
    def step():
        nonlocal n
        n += 1
        return n
    return step

c = make_counter()
print(c(), c())  # 1 2

Теперь про «глобалки между модулями». Это антипаттерн. Если в a.py написать from config import x, ты копируешь ССЫЛКУ на текущее значение. Переприсвоишь x в config — у тебя в a.py останется старое, потому что это уже отдельное имя.

Как правильно: импортируй модуль целиком и обращайся через него, либо явно передавай состояние.

# config.py
state = {'counter': 0}

# worker.py
import config
def bump():
    config.state['counter'] += 1   # все видят актуальное значение

Общее правило: global хорош для счётчиков-флагов в одном файле, но если состояние нужно многим — лучше передавать его аргументом или хранить в объекте/классе. Скрытые глобалки делают код непредсказуемым при тестах.

0

Маленькая, но спасительная деталь: global нужен только когда ты ПРИСВАИВАЕШЬ глобальной переменной. Читать и даже мутировать её можно без global:

items = []
def add(x):
    items.append(x)   # мутация — global НЕ нужен
add(1)
print(items)  # [1]

total = 0
def reset():
    total = 0         # это локальная total, снаружи 0 не изменится!

Именно из-за этой разницы между «мутирую объект» и «переприсваиваю имя» новички путаются. append/+= у списка меняют сам объект, а total = 0 создаёт новое локальное имя.

Ваш ответ

Войдите, чтобы ответить на вопрос.
Поддержать проект