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