← Все вопросы

Что такое пространство имён в Python и как работает правило LEGB при поиске переменных?

Задан 3 месяца назад1.3к просмотров2 ответа
6

Часто слышу «пространство имён» (namespace) и аббревиатуру LEGB, но цельной картины нет. Пытаюсь понять, почему вот тут Python берёт именно ту x, а не другую:

x = "глобальная"

def outer():
    x = "из outer"
    def inner():
        print(x)   # какую x он напечатает и почему?
    inner()

outer()

Объясните, что такое namespace и в каком порядке Python ищет имя.

2 ответа

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

Пространство имён (namespace) — это просто «словарь», связывающий имена с объектами. У модуля своё, у каждой функции при вызове своё, у встроенных функций своё. Когда ты пишешь x, Python должен решить, в каком namespace искать это имя. Делает он это по правилу LEGB — это порядок поиска:

  • L (Local) — локальные имена текущей функции;
  • E (Enclosing) — имена объемлющих функций (для вложенных функций/замыканий);
  • G (Global) — имена на уровне модуля;
  • B (Built-in) — встроенные (len, print, range, ...).

Поиск идёт строго в этом порядке: нашёл на первом подходящем уровне — остановился. В твоём примере inner() ищет x: локально в inner нет → смотрит Enclosing, то есть outer, там x = "из outer" — берёт её. Напечатает из outer. До глобальной x дело не дойдёт.

Если захочешь ПИСАТЬ во внешнюю переменную, а не читать, нужны ключевые слова:

x = "глобальная"

def outer():
    x = "из outer"
    def inner():
        nonlocal x          # пишем в x из enclosing (outer)
        x = "изменено inner"
    inner()
    print(x)                # 'изменено inner'

outer()
print(x)                    # 'глобальная' — глобальную не трогали

global x — писать в глобальную (модульную), nonlocal x — писать в ближайшую enclosing. Без них присваивание x = ... внутри функции просто создаёт новую ЛОКАЛЬНУЮ переменную, а внешнюю не меняет.

4

Классическая ловушка LEGB — UnboundLocalError:

x = 10
def f():
    print(x)   # UnboundLocalError!
    x = 5

Казалось бы, должна напечататься глобальная x = 10. Но раз внутри функции есть присваивание x = 5, Python ещё на этапе компиляции решает, что x — локальная на ВСЮ функцию. И на момент print она ещё не создана. Лечится либо global x (если правда нужна глобальная), либо переименованием. Полезно подсмотреть, что реально в namespace, через locals() и globals().

Ваш ответ

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