Python namespaces

Пространства имен создаются в разные моменты и имеют разное время жизни. Пространство имен, содержащее встроенные имена, создается при запуске интерпретатора Python и никогда не удаляется. Глобальное пространство имен для модуля создается при считывании определения модуля; обычно пространства имен модулей существуют до завершения работы интерпретатора. Операторы, выполняемые при вызове интерпретатора, либо считываеме из файла сценария, либо интерактивно, считаются частью модуля с именем __main__, поэтому у них есть собственное глобальное пространство имен. Встроенные имена фактически также находятся в модуле.

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

Область видимости (scope) - это текстовая область программы Python, где пространство имен доступно непосредственно. «Непосредственно доступный» здесь означает, что неквалифицированная ссылка на имя пытается найти имя в данном пространстве имен.

Хотя области видимости определяются статически, они используются динамически. В любой момент во время выполнения есть 3 или 4 вложенных области, чьи пространства имен доступны напрямую:

  • самая внутренняя (innermost) область видимости, которая ищется первой, содержит локальные имена
  • области видимости любых включающих функций (если таковые имеются), поиск которых начинается с ближайшей бласти видимости. Здесь содержатся нелокальные, но также и неглобальные имена
  • предпоследняя область содержит глобальные имена текущего модуля
  • самая внешняя область видимости (поиск обращается к ней в последюю очередь) - это пространство имен, содержащее встроенные имена

Если имя объявлено глобально, то тогда все ссылки и назначения переходят непосредственно в область, содержащую глобальные имена модуля. Чтобы повторно привязать переменные, которые необходимо видеть за пределами innermost области, можно использовать nonlocal; если они не объявлены нелокальными, эти переменные доступны только для чтения (попытка записи в такую переменную просто создаст новую локальную переменную во внутренней области видимости, а внешняя переменная с таким же именем останется неизменной).

Обычно локальная область видимости ссылается на локальные имена текущей (буквальной) функции. За пределами функций локальная область видимости ссылается на то же пространство имен, что и глобальная область видимости: на пространство имен модуля. Определения классов помещают еще одно пространство имен в локальную область видимости.

Важно понимать, что области видимости определяются буквально: глобальная область видимости функции, определенной в модуле, является пространством имен этого модуля, независимо от того, откуда и под каким псевдонимом вызывается функция. С другой стороны, фактический поиск имен выполняется динамически во время выполнения - однако определение языка эволюционирует в сторону статического разрешения имен во время «компиляции», поэтому не стоит полагаться на динамическое разрешение имен. Фактически, локальные переменные уже определены статически.

Особенность Python заключается в том, что если не действует ни один global или nonlocal оператор - присвоения имен всегда попадают в самую внутреннюю область видимости. Назначения не копируют данные - они просто связывают имена с объектами. То же самое верно и для удалений: оператор del x удаляет привязку x из пространства имен, на которое ссылается локальная область видимости. Фактически, все операции, которые вводят новые имена, используют локальную область видимости: в частности, операторы импорта и определения функций связывают имя модуля или функции в локальной области.

Оператор global может использоваться, чтобы указать, что определенные переменные находятся в глобальной области видимости и должны быть там повторно привязаны.

Пример

>>> def scope_test():
...     def do_local():
...         spam = "local spam"

...     def do_nonlocal():
...         nonlocal spam
...         spam = "nonlocal spam"

...     def do_global():
...         global spam
...         spam = "global spam"

...     spam = "test spam"
...     do_local()
...     print("After local assignment:", spam)
...     do_nonlocal()
...     print("After nonlocal assignment:", spam)
...     do_global()
...     print("After global assignment:", spam)

>>> spam = 'spam'
>>> scope_test()
After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
>>> print("In global scope:", spam)
In global scope: global spam

Статья целиком