Пространство имен и область видимости в классах
В определения классов происходят некоторые хитрые трюки с пространствами имен. Знание того, как работают пространства имен в объектах полезно для любого продвинутого программиста на Python.
Пространство имен — это отображение имен, определенных в объектах. Большинство пространств имен в настоящее время реализованы в виде словарей Python, но это обычно не заметно, кроме производительности, в будущем оно может измениться.
Примерами пространств имен являются:
- набор встроенных имен, содержащих такие функции, как abs() и имена встроенных исключений;
- глобальные переменные в модуле;
- локальные имена переменных в вызове функции.
В некотором смысле набор атрибутов объекта также образует пространство имен. Важно понимать о пространствах имен, то что между именами переменных в разных областях видимости нет абсолютно никакой связи. Например, два разных модуля могут определить функцию 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, в которой пространство имен доступно непосредственно. “Непосредственно доступный » здесь означает, что безусловная ссылка на имя пытается найти имя в пространстве имен.
Области видимости определяются статически, но используются динамически. В любой момент во время выполнения существует по крайней мере три вложенные области, пространства имен которых доступны непосредственно:
- Самая внутренняя область, которая ищется первой, содержит локальные имена. Области видимости любых вложенных функций, поиск которых начинается с ближайшей охватывающей области, содержит нелокальные, но также и неглобальные имена.
- Следующая за последней область содержит глобальные имена текущего модуля.
- Самая внешняя область (последний поиск) — это пространство имен, содержащее встроенные имена.
Если имя перемененной объявлено глобальным 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 в глобальной области видимости, прежде чем она вывелась на печать!
- ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
- Пространство имен и область видимости в классах
- Определение классов
- Объект класса и конструктор класса
- Создание экземпляра класса
- Метод экземпляра класса
- Что такое метод класса и зачем нужен
- Что такое статический метод в классах Python и зачем нужен
- Атрибуты класса и переменные экземпляра класса
- Кэширование методов экземпляра декоратором lru_cache
- Закрытые/приватные методы и переменные класса Python
- Наследование классов
- Множественное наследование классов
- Абстрактные классы
- Перегрузка методов в классе Python
- Что такое миксины и как их использовать
- Класс Python как структура данных, подобная языку C
- Создание пользовательских типов данных
- Специальные (магические) методы класса Python
- Базовая настройка классов Python магическими методами
- Настройка доступа к атрибутам класса Python
- Дескриптор класса для чайников
- Протокол дескриптора класса
- Практический пример дескриптора
- Использование метода .__new__() в классах Python
- Специальный атрибут __slots__ класса Python
- Специальный метод __init_subclass__ класса Python
- Определение метаклассов metaclass
- Эмуляция контейнерных типов в классах Python
- Другие специальные методы класса
- Как Python ищет специальные методы в классах
- Шаблон проектирования Фабрика и его реализация
Момент создания локального пространства имен для функции
Локальное пространство имен — это область видимости переменных и функций, которая доступна только внутри определенной функции или блока кода. В JavaScript, когда функция выполняется, создается локальное пространство имен, в котором определяются все локальные переменные и функции, объявленные внутри нее.
Ключевые моменты:
- Локальное пространство имен создается при вызове функции. Когда функция вызывается, она создает свое собственное локальное пространство имен, где могут быть объявлены локальные переменные и функции.
- Локальные переменные, объявленные внутри функции, видимы только внутри этой функции. Они не доступны за пределами функции или в других функциях.
- Каждый вызов функции создает свое собственное локальное пространство имен. Если функция вызывается несколько раз, каждый вызов создает свою собственную копию локальных переменных и функций.
- Локальные переменные имеют приоритет перед глобальными переменными с тем же именем. Если внутри функции определена локальная переменная с таким же именем, как глобальная переменная, то при обращении к этой переменной будет использоваться локальная, а не глобальная переменная.
Использование локальных пространств имен помогает избегать конфликтов имен между переменными и функциями, а также улучшает модульность и переиспользуемость кода. Кроме того, локальные переменные имеют более короткое время жизни, поскольку они создаются только при вызове функции и уничтожаются при завершении функции.
Когда функция начинает выполнение
Когда функция вызывается в программе, происходит несколько ключевых моментов, приводящих к ее выполнению:
- Создание локального пространства имен для функции
- Привязка параметров и аргументов
- Выполнение кода функции
- Возвращение значения (при необходимости)
Создание локального пространства имен для функции
Перед выполнением кода функции, интерпретатор создает локальное пространство имен, где будут сохраняться все локальные переменные и другие объекты, созданные внутри функции. Это сделано для изоляции функции от глобального пространства имен, чтобы переменные с одинаковыми именами не конфликтовали между собой.
Привязка параметров и аргументов
Параметры функции — это имена, указанные в ее определении, которые она ожидает получить в качестве входных данных. Аргументы — это значения, переданные при вызове функции. При вызове функции, аргументы связываются с параметрами путем присваивания значений. Это позволяет использовать переданные значения внутри функции, и они становятся доступными по именам параметров.
Выполнение кода функции
После привязки параметров и аргументов, выполняется код функции. В этот момент интерпретатор последовательно выполняет инструкции, находящиеся внутри функции. Здесь можно использовать параметры, аргументы и другие переменные, объявленные внутри функции.
Возвращение значения
Возвращение значения — это опциональный шаг, который может происходить в конце выполнения функции. Если функция имеет оператор return, то она может вернуть значение обратно в вызывающую ее программу. Если оператор return не указан, функция вернет специальное значение None по умолчанию.
Все эти моменты вместе обеспечивают выполнение функции при вызове. Каждый раз, когда функция вызывается в программе, интерпретатор выполняет эти шаги, что позволяет использовать функции для структурирования кода и повторного использования логики.
Вопрос-ответ
Как работает локальное пространство имен в функции?
Локальное пространство имен в функции создается в момент вызова функции и уничтожается при завершении работы функции. Оно позволяет изолировать переменные и функции, объявленные внутри функции, от внешнего кода.
Когда именно создается локальное пространство имен для функции?
Локальное пространство имен для функции создается в момент вызова функции. Это происходит тогда, когда исполнение программы достигает строки, содержащей вызов функции.
Что происходит с локальным пространством имен после завершения работы функции?
После завершения работы функции локальное пространство имен уничтожается. Все переменные, объявленные внутри функции, исчезают, и к ним нельзя обратиться извне функции.
Могут ли переменные внутри функции иметь те же имена, что и переменные внешнего кода?
Да, переменные внутри функции могут иметь те же имена, что и переменные внешнего кода, но это не вызывает конфликта имен. Внутренние переменные имеют приоритет перед внешними и видны только внутри функции.
Каким образом локальное пространство имен для функции может быть полезным при написании кода?
Локальное пространство имен для функции полезно, когда нужно изолировать переменные и функции, чтобы они не конфликтовали с внешним кодом. Оно также позволяет упростить код и улучшить его читаемость, так как внутренние переменные не доступны извне функции.
9.2. Области видимости и пространства имён
Перед тем, как знакомиться с классами, следует рассказать о правилах языка, касающихся областей видимости. Определения классов выполняют несколько изящных приемов с пространствами имён, и Вам следует знать о работе областей видимости и пространств имён, для полного понимания происходящего.
Начнём с нескольких определений. Пространство имён определяет отображение имён в объекты. Большинство пространств имен в языке Python реализованы как словари, что, однако, никак себя не проявляет (кроме производительности) и может быть изменено в будущем. Вот несколько примеров пространств имён: множество встроенных имён (функции, исключения), глобальные имена в модуле, локальные имена при вызове функций. В этом смысле множество атрибутов объекта также образует пространство имён. Важно понимать, что между именами в разных пространствах имён нет связи. Например, два модуля могут определить функции с именем «maximize» не создавая при этом путаницы — пользователь должен ссылаться на них с использованием имени модуля в качестве приставки.
Под словом атрибут мы подразумеваем любое имя, следующее после точки: например, в выражении z.real , real является атрибутом объекта z . Строго говоря, имена в модулях являются атрибутами модуля: в выражении modname.funcname , modname является объектом-модулем и funcname является его атрибутом. В этом случае имеет место прямое соответствие между атрибутами модуля и глобальными именами, определёнными в модуле: они совместно используют одно пространство имён 1 !
Атрибуты могут быть доступны только для чтения, а могут и допускать присваивание. Во втором случае Вы можете записать ‘modname.attribute = 42′ или даже удалить его, используя инструкцию del : ‘del modname.attribute‘ .
Пространства имён создаются в различные моменты времени и имеют разную продолжительность жизни. Пространство имён, содержащее встроенные имена, создаётся при запуске интерпретатора и существует всё время его работы. Глобальное пространство имён модуля создаётся, когда он считывается, и, обычно, также существует до завершения работы интерпретатора. Инструкции, выполняемые на верхнем уровне, т.е. читаемые из файла-сценария или интерактивно, рассматриваются как часть модуля __main__ , имеющего собственное глобальное пространство имён. (В действительности, встроенные имена также находятся в модуле — __builtin__ ).
Локальное пространство имён функции создается при вызове функции и удаляется при выходе из неё (возвращается значение или генерируется исключение, которое не обрабатывается внутри функции). Безусловно, при рекурсивном вызове создаётся собственное локальное пространство имен для каждого вызова.
Область видимости — фрагмент программы, в котором пространство имён непосредственно доступно, то есть нет необходимости в использовании записи через точку для того, чтобы поиск имени производился в данном пространстве имён.
Несмотря на статическое определение, области видимости используются динамически. В любой момент времени выполнения программы используется ровно три вложенных области видимости (три непосредственно доступных пространства имен). Сначала поиск имени производится во внутренней области видимости, содержащей локальные имена. Далее — в средней, содержащей глобальные имена модуля. И, наконец, во внешней, содержащей встроенные имена.
Обычно локальная область видимости соответствует локальному пространству имён текущей функции (класса, метода). За пределами функции (класса, метода), локальная область видимости соответствует тому же пространству имён, что и глобальная: пространству имён текущего модуля.
Важно понимать, что область видимости определяется по тексту: глобальная область видимости функции, определенной в модуле — пространство имён этого модуля, независимо от того, откуда или под каким псевдонимом функция была вызвана. С другой стороны, реально поиск имён происходит динамически, во время выполнения. Однако язык развивается в сторону статического разрешения имён, определяемого во время «компиляции», поэтому не стоит полагаться на динамическое разрешение имён! (Фактически, локальные переменные уже определяются статически.)
В языке Python есть особенность: присваивание всегда производится имени в локальной области видимости, если перед этим не было указано явно (инструкция global ), что переменная находится в глобальной области видимости. Присваивание не копирует данные — оно только привязывает имя к объекту. То же самое верно и для удаления: инструкция ‘del x’ удаляет имя x из пространства имён, соответствующего локальной области видимости. В сущности, все операции, которые вводят новые имена, используют локальную область. Так, импортирование модуля и определение функции привязывают модуль или функцию к локальной области видимости.
- Есть одно исключение. Объект-модуль имеет секретный атрибут __dict__, содержащий словарь, используемый для реализации пространства имён модуля. Имя __dict__ является атрибутом, однако не является глобальным именем. Не следует использовать атрибут __dict__ где-либо кроме отладчиков, так как это нарушает абстракцию реализации пространства имён.
Что такое пространства имен Python (и зачем они нужны?)
Конфликты имен все время происходят в реальной жизни. Например, в каждой школе, в которой я когда-либо учился, было по крайней мере два ученика в моем классе с одним и тем же именем. Если кто-то вошел в класс и попросил ученика X, мы с энтузиазмом спросили: «О каком из них вы говорите? Есть два ученика по имени X. «После этого интересующийся человек предоставил бы нам фамилию, и мы указали бы ему верного X.
Всей этой путаницы с именами можно избежать, если у каждого было бы уникальное имя. Это не проблема в классе из 30 студентов. Тем не менее, становится все труднее придумать уникальное, содержательное и легко запоминаемое имя для каждого ребенка в школе, городе, стране или во всем мире. Другая проблема в предоставлении каждому ребенку уникального имени заключается в том, что процесс определения того, кто-то еще назвал своего ребенка, Мейси, Маки или Маси, может быть очень утомительным.
Подобный конфликт может также возникнуть в программировании. Когда вы пишете программу из 30 строк без внешних зависимостей, очень легко дать уникальные и значимые имена всем вашим переменным. Проблема возникает, когда в программе есть тысячи строк, и вы также загрузили некоторые внешние модули. В этом уроке вы узнаете о пространствах имен, их важности и разрешении области видимости в Python.
Что такое пространство имен?
Пространство имен — это в основном система, которая гарантирует, что все имена в программе уникальны и могут использоваться без каких-либо конфликтов. Возможно, вы уже знаете, что все в Python строки, списки, функции и т.д. — это объекты. Еще один интересный факт: Python реализует пространства имен как словари. Существует сопоставление «имя-объект» с именами в виде ключей и объектов в качестве значений. Несколько пространств имен могут использовать одно и то же имя и сопоставлять их с другим объектом. Вот несколько примеров пространств имен:
- Локальное пространство имен: это пространство имен содержит локальные имена внутри функции. Это пространство имен создается при вызове функции и продолжается до тех пор, пока функция не вернется.
- Глобальное пространство имен: это пространство имен, которое включает имена из различных импортированных модулей, которые вы используете в проекте. Оно создается, когда модуль включен в проект, и оно существует до завершения скрипта.
- Встроенное пространство имен: это пространство имен содержит встроенные функции и встроенные имена исключений.
В математических модулях Python на Envato Tuts + я написал о полезных математических функциях, доступных в разных модулях. Например, модули math и cmath имеют множество функций, которые являются общими для обоих из них, такие как log10() , acos() , cos() , exp() и т.д. Если вы используете оба этих модуля в одной и той же программе, единственный способ использовать эти функции с определенностью — это префикс им имени модуля, например, math.log10() и cmath.log10() .
Что такое область?
Пространства имен помогают нам однозначно идентифицировать все имена внутри программы. Однако это не означает, что мы можем использовать имя переменной везде, где захотим. Имя также имеет область действия, определяющую части программы, в которых вы можете использовать это имя без использования префикса. Так же, как пространства имен, в программе также есть несколько областей. Вот список некоторых областей, которые могут существовать во время выполнения программы.
- Локальная область, которая является самой внутренней областью, которая содержит список локальных имен, доступных в текущей функции.
- Область всех закрывающих функций. Поиск имени начинается с ближайшей охватывающей области и перемещается наружу.
- Область уровня модуля, содержащая все глобальные имена из текущего модуля.
- Это область, которая содержит список всех встроенных имен. Поиск в этой области выполняется последним.
В следующих разделах этого руководства мы будем широко использовать встроенную функцию dir() Python, чтобы вернуть список имен в текущей локальной области. Это поможет вам более четко понять концепцию пространств имен и области.
Разрешение области
Как я упоминал в предыдущем разделе, поиск данного имени начинается с самой внутренней функции, а затем движется все выше и выше, пока программа не сможет сопоставить это имя с объектом. Если такое имя не найдено ни в одном из пространств имен, программа вызывает исключение NameError.
Прежде чем начать, попробуйте ввести dir() в IDLE или любой другой Python IDE.
# ['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
Все эти имена, перечисленные в каталоге dir() , доступны в каждой программе Python. Для краткости я начну ссылаться на них как ‘__builtins __’. ‘__ spec__’ в остальных примерах.
Давайте посмотрим на результат функции dir() после определения переменной и функции.
a_num = 10