Сообщить об ошибке.

Пространство имен и область видимости в классах

В определения классов происходят некоторые хитрые трюки с пространствами имен. Знание того, как работают пространства имен в объектах полезно для любого продвинутого программиста на Python.

Пространство имен - это отображение имен, определенных в объектах. Большинство пространств имен в настоящее время реализованы в виде словарей Python, но это обычно не заметно, кроме производительности, в будущем оно может измениться.

Примерами пространств имен являются:

В некотором смысле набор атрибутов объекта также образует пространство имен. Важно понимать о пространствах имен, то что между именами переменных в разных областях видимости нет абсолютно никакой связи. Например, два разных модуля могут определить функцию maximize без путаницы, пользователи модулей должны использовать точечную нотацию, для извлечения ее значения из модуля.

В языке Python используется слово атрибут для любого имени, следующего за точкой. Например, в выражении z.real, real - это атрибут объекта z. Строго говоря, ссылки на имена переменных, классов и функций в модулях - это ссылки на атрибуты. В выражении modname.funcname, modname - это объект модуля, а funcname - его атрибут. В этом случае происходит прямое сопоставление между атрибутами модуля и глобальными именами, определенными в модуле, они используют одно и то же пространство имен!

Атрибуты могут быть доступны для чтения или записи. В последнем случае возможно присвоение значений атрибутам. Для атрибутов модуля, доступных для записи можно написать modname.the_answer = 42. Атрибуты, доступные для записи, также могут быть удалены с помощью оператора del. Например, del modname.the_answer удаляет атрибут the_answer из объекта с именем modname.

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

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

Область видимости - это текстовая область программы Python, в которой пространство имен доступно непосредственно. “Непосредственно доступный " здесь означает, что безусловная ссылка на имя пытается найти имя в пространстве имен.

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

  1. Самая внутренняя область, которая ищется первой, содержит локальные имена. Области видимости любых вложенных функций, поиск которых начинается с ближайшей охватывающей области, содержит нелокальные, но также и неглобальные имена.
  2. Следующая за последней область содержит глобальные имена текущего модуля.
  3. Самая внешняя область (последний поиск) - это пространство имен, содержащее встроенные имена.

Если имя перемененной объявлено глобальным global, то все ссылки и присвоения переходят непосредственно в среднюю область - [2], содержащую глобальные имена модуля. Для повторного связывания переменных, найденных вне внутренней области (вложенные функции), можно использовать нелокальный оператор nonlocal. Если переменные не являются локальными, то эти переменные доступны только для чтения. Попытка записи в такую переменную просто создаст новую локальную переменную в самой внутренней области, оставив внешнюю переменную с идентичным именем неизменной.

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

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

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

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

Пример:

Пример отчетливо показывает динамическую сущность языка Python, последовательность поиска имен переменных, а так же демонстрирует, как ссылаться на различные области видимости в пространстве имен.

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)

scope_test()
print("In global scope:", spam)

Выходные данные примера кода:

# After local assignment: test spam
# After nonlocal assignment: nonlocal spam
# After global assignment: nonlocal spam
# In global scope: global spam

Обратите внимание, что локальное присвоение spam = "local spam" во внутренней функции do_local() не изменило значение spam, присвоенное во внешней функции scope_test. Нелокальное nonlocal присвоение spam = "nonlocal spam" во внутренней функции do_nonlocal() изменило значение spam, а глобальное global присвоение spam = "global spam" во внутренней функции do_global() изменило значение spam на уровне модуля.

Обратите внимание, что перед выполнением функции scope_test() не было никакого присвоения значения для переменной spam. Использование оператора global во внутренней функции do_global() восстановил переменную spam в глобальной области видимости, прежде чем она вывелась на печать!