5. Система импорта¶
Код Python в одном модуле получает доступ к коду в другом модуле в процессе его импортирования. Инструкция import — самый распространённый способ вызвать механизм импорта, но не единственный. Для этого также можно использовать, например, функцию importlib.import_module() или встроенную функцию __import__().
Инструкция import объединяет две операции: она ищет модуль с указанным именем, а затем связывает результат поиска с именем в локальной области видимости. Операция поиска в инструкции import определяется как вызов функции __import__() с соответствующими аргументами. Возвращаемое значение __import__() используется для выполнения операции связывания имени в инструкции import. Точные сведения об этой операции связывания имени см. в описании инструкции import.
Прямой вызов __import__() выполняет только поиск модуля и, если он найден, создание объекта модуля. Могут возникать некоторые побочные эффекты, например импорт родительских пакетов и обновление различных кешей (включая sys.modules), но операцию связывания имени выполняет только инструкция import.
Когда выполняется инструкция import, вызывается стандартная встроенная функция __import__(). Другие механизмы вызова системы импорта (например, importlib.import_module()) могут обойти __import__() и реализовать семантику импорта собственными средствами.
Когда модуль импортируется впервые, Python ищет его и, если находит, создаёт объект модуля [1] и инициализирует его. Если модуль с указанным именем найти не удаётся, возбуждается исключение ModuleNotFoundError. При вызове механизма импорта Python использует различные стратегии поиска модуля с указанным именем. Эти стратегии можно изменять и расширять с помощью различных обработчиков импорта, описанных в разделах ниже.
Изменено в версии 3.3: Система импорта была обновлена и теперь полностью реализует вторую фазу PEP 302. Неявного механизма импорта больше нет — вся система импорта доступна через sys.meta_path. Кроме того, реализована встроенная поддержка пакетов пространства имён (см. PEP 420).
5.1. importlib¶
Модуль importlib предоставляет богатый API для взаимодействия с системой импорта. Например, importlib.import_module() — рекомендуемый API для вызова механизма импорта, более простой, чем у встроенной функции __import__(). Дополнительные сведения см. в документации библиотеки importlib.
5.2. Пакеты¶
В Python есть только один тип объектов модулей, и все модули принадлежат к этому типу независимо от того, реализованы ли они на Python, на C или на чём-то ещё. Чтобы помочь организовать модули и обеспечить иерархию имён, в Python есть понятие пакетов.
Пакеты можно представлять как каталоги в файловой системе, а модули — как файлы внутри каталогов, но не стоит понимать эту аналогию слишком буквально: пакеты и модули не обязаны происходить из файловой системы. В этой документации мы будем использовать эту удобную аналогию с каталогами и файлами. Как и каталоги файловой системы, пакеты организованы иерархически, и пакеты сами могут содержать подпакеты, а также обычные модули.
Важно помнить, что все пакеты являются модулями, но не все модули являются пакетами. Иначе говоря, пакеты — это просто особый вид модулей. А именно, любой модуль, содержащий атрибут __path__, считается пакетом.
У всех модулей есть имя. Имена подпакетов отделяются от имени родительского пакета точкой, подобно стандартному синтаксису доступа к атрибутам в Python. Так, у вас может быть пакет email, у которого, в свою очередь, есть подпакет email.mime, а в этом подпакете — модуль email.mime.text.
5.2.1. Обычные пакеты¶
Python определяет два типа пакетов: обычные пакеты и пакеты пространства имён. Обычные пакеты — это традиционные пакеты, какими они были в Python 3.2 и более ранних версиях. Обычный пакет, как правило, реализуется в виде каталога, содержащего файл __init__.py. При импорте обычного пакета этот файл __init__.py неявно выполняется, а определённые в нём объекты связываются с именами в пространстве имён пакета. Файл __init__.py может содержать тот же код Python, что и любой другой модуль, а при импорте Python добавит к модулю некоторые дополнительные атрибуты.
Например, следующая структура файловой системы определяет пакет верхнего уровня parent с тремя подпакетами:
parent/
__init__.py
one/
__init__.py
two/
__init__.py
three/
__init__.py
Импорт parent.one неявно выполнит parent/__init__.py и parent/one/__init__.py. Последующие импорты parent.two или parent.three выполнят parent/two/__init__.py и parent/three/__init__.py соответственно.
Подкаталог внутри обычного пакета, не содержащий файла __init__.py, рассматривается как неявный пакет пространства имён («подпакет пространства имён») с корнем в этом родительском пакете. Лежащую в основе спецификацию см. в PEP 420.
5.2.2. Пакеты пространства имён¶
Пакет пространства имён составлен из различных частей, каждая из которых вносит в родительский пакет один подпакет. Части могут находиться в разных местах файловой системы. Их можно также найти в zip-файлах, в сети или где-либо ещё, где Python выполняет поиск при импорте. Пакеты пространства имён могут как напрямую соответствовать объектам файловой системы, так и не соответствовать им. Они могут быть виртуальными модулями, не имеющими конкретного представления.
Пакеты пространства имён не используют для своего атрибута __path__ обычный список. Вместо этого они используют собственный итерируемый тип, который при следующей попытке импорта внутри этого пакета автоматически выполняет новый поиск частей пакета, если путь их родительского пакета (или sys.path для пакета верхнего уровня) изменился.
У пакетов пространства имён нет файла parent/__init__.py. Более того, при поиске для импорта может найтись несколько каталогов parent, каждый из которых предоставлен отдельной частью. Поэтому parent/one может физически находиться не рядом с parent/two. В этом случае Python создаёт пакет пространства имён для пакета верхнего уровня parent всякий раз, когда импортируется он сам или один из его подпакетов.
Пакеты пространства имён могут также быть вложены в обычный пакет. Когда система импорта просматривает __path__ обычного пакета и встречает подкаталог, не содержащий файла __init__.py, этот подкаталог становится частью, вносящей вклад в подпакет пространства имён охватывающего обычного пакета.
См. также PEP 420 — спецификацию пакетов пространства имён.
5.3. Поиск¶
Чтобы начать поиск, Python нужно полное квалифицированное имя импортируемого модуля (или пакета, но для целей этого обсуждения разница несущественна). Это имя может поступать из различных аргументов инструкции import или из параметров функций importlib.import_module() и __import__().
Это имя будет использоваться на различных этапах поиска при импорте и может быть путём к подмодулю в точечной нотации, например foo.bar.baz. В этом случае Python сначала пытается импортировать foo, затем foo.bar и, наконец, foo.bar.baz. Если какой-либо из промежуточных импортов завершается неудачей, возбуждается исключение ModuleNotFoundError.
5.3.1. Кеш модулей¶
Первое место, которое проверяется при поиске для импорта, — sys.modules. Это отображение служит кешем всех ранее импортированных модулей, включая промежуточные пути. Так, если ранее был импортирован foo.bar.baz, то sys.modules будет содержать записи для foo, foo.bar и foo.bar.baz. Значением каждого ключа служит соответствующий объект модуля.
При импорте имя модуля ищется в sys.modules, и если оно там есть, то связанное с ним значение и есть модуль, удовлетворяющий импорту, а процесс на этом завершается. Однако если значение равно None, возбуждается исключение ModuleNotFoundError. Если имя модуля отсутствует, Python продолжит поиск этого модуля.
sys.modules доступен для записи. Удаление ключа не обязательно уничтожит связанный с ним модуль (поскольку другие модули могут хранить на него ссылки), но сделает недействительной запись кеша для этого модуля, и при следующем импорте Python будет искать его заново. Ключу также можно присвоить None, чтобы следующий импорт модуля завершился исключением ModuleNotFoundError.
Но будьте осторожны: если вы сохраните ссылку на объект модуля, сделаете недействительной его запись в sys.modules, а затем заново импортируете этот модуль, то два объекта модуля не будут одним и тем же объектом. Напротив, importlib.reload() использует тот же самый объект модуля и просто заново инициализирует его содержимое, повторно выполняя код модуля.
5.3.2. Поисковики и загрузчики¶
Если модуль с заданным именем не найден в sys.modules, вызывается протокол импорта Python, чтобы найти и загрузить этот модуль. Протокол состоит из двух концептуальных объектов — поисковиков и загрузчиков. Задача поисковика — определить, может ли он найти модуль с заданным именем с помощью известной ему стратегии. Объекты, реализующие оба этих интерфейса, называются импортёрами: они возвращают сами себя, когда обнаруживают, что могут загрузить запрошенный модуль.
В Python есть несколько поисковиков и импортёров по умолчанию. Первый умеет находить встроенные модули, второй — замороженные. Третий поисковик по умолчанию ищет модули по пути импорта. Путь импорта — это список местоположений, которые могут указывать на пути файловой системы или zip-файлы. Его можно расширить так, чтобы поиск охватывал любой ресурс, местоположение которого можно определить, например ресурсы, заданные URL-адресами.
Механизм импорта можно дополнять: чтобы расширить диапазон и область поиска модулей, достаточно добавить новые поисковики.
Поисковики на самом деле не загружают модули. Если они могут найти модуль с заданным именем, то возвращают спецификацию модуля — инкапсулированную информацию о модуле, связанную с его импортом, которую затем использует механизм импорта при загрузке модуля.
В следующих разделах подробнее описан протокол поисковиков и загрузчиков, включая то, как создавать и регистрировать новые, чтобы расширить механизм импорта.
Изменено в версии 3.4: В предыдущих версиях Python поисковики возвращали загрузчики напрямую, тогда как теперь они возвращают спецификации модулей, которые содержат загрузчики. Загрузчики по-прежнему используются при импорте, но у них стало меньше обязанностей.
5.3.3. Обработчики импорта¶
Механизм импорта спроектирован так, чтобы его можно было дополнять, и главным инструментом для этого служат обработчики импорта. Существует два типа обработчиков импорта: мета-обработчики и обработчики пути импорта.
Мета-обработчики вызываются в начале обработки импорта, до любой другой обработки, кроме поиска в кеше sys.modules. Это позволяет им переопределять обработку sys.path, обработку замороженных и даже встроенных модулей. Мета-обработчики регистрируются добавлением новых объектов-поисковиков в sys.meta_path, как описано ниже.
Обработчики пути импорта вызываются как часть обработки sys.path (или package.__path__) в тот момент, когда встречается связанный с ними элемент пути. Обработчики пути импорта регистрируются добавлением новых вызываемых объектов в sys.path_hooks, как описано ниже.
5.3.4. Мета-путь¶
Если модуль с заданным именем не найден в sys.modules, Python затем просматривает sys.meta_path, содержащий список объектов поисковиков мета-пути. Эти поисковики опрашиваются по порядку, чтобы выяснить, умеют ли они обрабатывать модуль с заданным именем. Поисковики мета-пути должны реализовывать метод find_spec(), принимающий три аргумента: имя, путь импорта и (необязательно) целевой модуль. Поисковик мета-пути может использовать любую стратегию, чтобы определить, может ли он обработать модуль с заданным именем.
Если поисковик мета-пути умеет обрабатывать модуль с заданным именем, он возвращает объект спецификации. Если он не может обработать модуль, он возвращает None. Если обработка sys.meta_path доходит до конца списка и спецификация не возвращена, возбуждается исключение ModuleNotFoundError. Любые другие возбуждённые исключения просто передаются выше, прерывая процесс импорта.
Метод find_spec() поисковиков мета-пути вызывается с двумя или тремя аргументами. Первый — полное квалифицированное имя импортируемого модуля, например foo.bar.baz. Второй аргумент — элементы пути, используемые для поиска модуля. Для модулей верхнего уровня второй аргумент равен None, а для подмодулей или подпакетов — значению атрибута __path__ родительского пакета. Если нужный атрибут __path__ недоступен, возбуждается исключение ModuleNotFoundError. Третий аргумент — существующий объект модуля, который позднее станет целью загрузки. Система импорта передаёт целевой модуль только при перезагрузке.
Мета-путь может обходиться несколько раз для одного запроса на импорт. Например, если предположить, что ни один из задействованных модулей ещё не был кеширован, то импорт foo.bar.baz сначала выполнит импорт верхнего уровня, вызывая mpf.find_spec("foo", None, None) у каждого поисковика мета-пути (mpf). После того как foo будет импортирован, foo.bar будет импортирован вторым обходом мета-пути с вызовом mpf.find_spec("foo.bar", foo.__path__, None). Когда foo.bar будет импортирован, последний обход вызовет mpf.find_spec("foo.bar.baz", foo.bar.__path__, None).
Некоторые поисковики мета-пути поддерживают только импорт верхнего уровня. Эти импортёры всегда возвращают None, если в качестве второго аргумента передано что-либо, кроме None.
В sys.meta_path по умолчанию в Python есть три поисковика мета-пути: один умеет импортировать встроенные модули, другой — замороженные, а третий — модули из пути импорта (то есть поисковик на основе пути).
Изменено в версии 3.4: Метод find_spec() поисковиков мета-пути заменил find_module(), который теперь объявлен устаревшим. Он продолжит работать без изменений, но механизм импорта будет пробовать его, только если поисковик не реализует find_spec().
Изменено в версии 3.10: Использование find_module() системой импорта теперь возбуждает ImportWarning.
Изменено в версии 3.12: Метод find_module() удалён. Вместо него используйте find_spec().
5.4. Загрузка¶
Если и когда спецификация модуля найдена, механизм импорта использует её (и содержащийся в ней загрузчик) при загрузке модуля. Вот приблизительное описание того, что происходит на этапе загрузки при импорте:
module = None
if spec.loader is not None and hasattr(spec.loader, 'create_module'):
# Предполагается, что у загрузчика также определён 'exec_module'.
module = spec.loader.create_module(spec)
if module is None:
module = ModuleType(spec.name)
# Здесь устанавливаются атрибуты модуля, связанные с импортом:
_init_module_attrs(spec, module)
if spec.loader is None:
# не поддерживается
raise ImportError
sys.modules[spec.name] = module
try:
spec.loader.exec_module(module)
except BaseException:
try:
del sys.modules[spec.name]
except KeyError:
pass
raise
return sys.modules[spec.name]
Обратите внимание на следующие детали:
Если в
sys.modulesесть существующий объект модуля с заданным именем, то импорт к этому моменту уже вернул его.Модуль появляется в
sys.modulesдо того, как загрузчик выполнит код модуля. Это принципиально важно, потому что код модуля может (прямо или косвенно) импортировать сам себя; добавление его вsys.modulesзаранее предотвращает неограниченную рекурсию в худшем случае и многократную загрузку в лучшем.Если загрузка завершается неудачей, сбойный модуль — и только он — удаляется из
sys.modules. Любой модуль, уже находившийся в кешеsys.modules, а также любой модуль, успешно загруженный как побочный эффект, должен оставаться в кеше. Это отличается от перезагрузки, при которой вsys.modulesостаётся даже сбойный модуль.После создания модуля, но до его выполнения механизм импорта устанавливает атрибуты модуля, связанные с импортом («_init_module_attrs» в примере псевдокода выше), как кратко описано в разделе ниже.
Выполнение модуля — ключевой момент загрузки, в котором заполняется пространство имён модуля. Выполнение целиком делегируется загрузчику, который решает, что и как будет заполнено.
Модуль, созданный при загрузке и переданный в exec_module(), может не совпадать с тем, который возвращается в конце импорта [2].
Изменено в версии 3.4: Система импорта взяла на себя шаблонные обязанности загрузчиков. Ранее их выполнял метод importlib.abc.Loader.load_module.
Изменено в версии 3.15: Метод load_module больше не используется.
5.4.1. Загрузчики¶
Загрузчики модулей обеспечивают важнейшую функцию загрузки — выполнение модуля. Механизм импорта вызывает метод importlib.abc.Loader.exec_module() с единственным аргументом — объектом модуля, который нужно выполнить. Любое значение, возвращённое из exec_module(), игнорируется.
Загрузчики должны удовлетворять следующим требованиям:
Если модуль является модулем Python (в отличие от встроенного модуля или динамически загружаемого расширения), загрузчик должен выполнять код модуля в глобальном пространстве имён модуля (
module.__dict__).Если загрузчик не может выполнить модуль, он должен возбудить исключение
ImportError, хотя любое другое исключение, возбуждённое при вызовеexec_module(), будет передано выше.
Во многих случаях поисковик и загрузчик могут быть одним и тем же объектом; тогда метод find_spec() просто возвращал бы спецификацию, в которой загрузчиком назначен self.
Загрузчики модулей могут по желанию создавать объект модуля во время загрузки, реализовав метод create_module(). Он принимает один аргумент — спецификацию модуля — и возвращает новый объект модуля, который будет использоваться при загрузке. create_module() не обязан устанавливать какие-либо атрибуты объекта модуля. Если метод возвращает None, механизм импорта создаст новый модуль самостоятельно.
Добавлено в версии 3.4: Метод create_module() загрузчиков.
Изменено в версии 3.4: Метод importlib.abc.Loader.load_module был заменён методом exec_module(), а механизм импорта взял на себя все шаблонные обязанности по загрузке.
Для совместимости с существующими загрузчиками механизм импорта будет использовать метод load_module() загрузчика, если он существует, а метод exec_module() загрузчик не реализует. Однако метод load_module() объявлен устаревшим, и вместо него загрузчикам следует реализовывать exec_module().
Метод load_module() должен реализовывать всю описанную выше шаблонную функциональность загрузки в дополнение к выполнению модуля. Действуют все те же ограничения с некоторыми дополнительными уточнениями:
Если в
sys.modulesесть существующий объект модуля с заданным именем, загрузчик должен использовать этот существующий модуль. (Иначеimportlib.reload()не будет работать правильно.) Если модуля с заданным именем вsys.modulesнет, загрузчик должен создать новый объект модуля и добавить его вsys.modules.Модуль должен существовать в
sys.modulesдо того, как загрузчик выполнит код модуля, чтобы предотвратить неограниченную рекурсию или многократную загрузку.Если загрузка завершается неудачей, загрузчик должен удалить все модули, которые он вставил в
sys.modules, но удалять он должен только сбойные модули, и только если загрузчик сам явно загрузил эти модули.
Изменено в версии 3.5: Возбуждается DeprecationWarning, когда определён exec_module(), но не определён create_module().
Изменено в версии 3.6: Возбуждается ImportError, когда определён exec_module(), но не определён create_module().
Изменено в версии 3.10: Использование load_module() будет возбуждать ImportWarning.
5.4.2. Подмодули¶
Когда подмодуль загружается любым способом (например, через API importlib, инструкции import или import-from, либо встроенную функцию __import__()), в пространстве имён родительского модуля имя подмодуля связывается с его объектом. Например, если у пакета spam есть подмодуль foo, то после импорта spam.foo у spam будет атрибут foo, связанный с подмодулем. Допустим, у вас такая структура каталогов:
spam/
__init__.py
foo.py
и в spam/__init__.py есть следующая строка:
from .foo import Foo
то выполнение следующего кода связывает в модуле spam имена foo и Foo:
>>> import spam
>>> spam.foo
<module 'spam.foo' from '/tmp/imports/spam/foo.py'>
>>> spam.Foo
<class 'spam.foo.Foo'>
Учитывая привычные правила связывания имён в Python, это может показаться неожиданным, но на самом деле это фундаментальная особенность системы импорта. Выполняется следующий инвариант: если у вас есть sys.modules['spam'] и sys.modules['spam.foo'] (как после приведённого выше импорта), то последний должен присутствовать как атрибут foo первого.
5.4.3. Спецификации модулей¶
Механизм импорта использует различную информацию о каждом модуле во время импорта, особенно перед загрузкой. Большая часть этой информации одинакова для всех модулей. Назначение спецификации модуля — инкапсулировать эту связанную с импортом информацию для каждого модуля в отдельности.
Использование спецификации при импорте позволяет передавать состояние между компонентами системы импорта, например между поисковиком, создающим спецификацию модуля, и загрузчиком, который его выполняет. Важнее всего то, что это позволяет механизму импорта выполнять шаблонные операции загрузки, тогда как без спецификации модуля эта обязанность лежала на загрузчике.
Спецификация модуля доступна как module.__spec__. Корректная установка __spec__ одинаково относится и к модулям, инициализируемым при запуске интерпретатора. Единственное исключение — __main__, где __spec__ в некоторых случаях устанавливается в None.
Подробности о содержимом спецификации модуля см. в ModuleSpec.
Добавлено в версии 3.4.
5.4.4. Атрибуты __path__ у модулей¶
Атрибут __path__ должен быть (возможно, пустой) последовательностью строк, перечисляющих места, где будут найдены подмодули пакета. По определению, если у модуля есть атрибут __path__, он является пакетом.
Атрибут __path__ пакета используется при импорте его подпакетов. Внутри механизма импорта он работает почти так же, как sys.path, то есть предоставляет список мест для поиска модулей при импорте. Однако __path__ обычно гораздо более ограничен, чем sys.path.
К атрибуту __path__ пакета применяются те же правила, что используются для sys.path. При обходе __path__ пакета опрашиваются обработчики sys.path_hooks (описанные ниже).
Файл __init__.py пакета может устанавливать или изменять атрибут __path__ пакета, и именно так обычно реализовывались пакеты пространства имён до PEP 420. С принятием PEP 420 пакетам пространства имён больше не нужно предоставлять файлы __init__.py, содержащие только код манипуляции с __path__; механизм импорта автоматически устанавливает __path__ правильно для пакета пространства имён.
5.4.5. Представления модулей¶
По умолчанию у всех модулей есть пригодное для использования представление, однако в зависимости от описанных выше атрибутов и спецификации модуля можно более явно управлять представлением объектов модулей.
Если у модуля есть спецификация (__spec__), механизм импорта попытается сгенерировать из неё представление. Если это не удаётся или спецификации нет, система импорта создаст представление по умолчанию, используя любую информацию, доступную в модуле. Она попытается использовать module.__name__, module.__file__ и module.__loader__ в качестве входных данных для представления, подставляя значения по умолчанию вместо недостающей информации.
Вот точные правила, которые применяются:
Если у модуля есть атрибут
__spec__, для генерации представления используется информация из спецификации. Учитываются атрибуты «name», «loader», «origin» и «has_location».Если у модуля есть атрибут
__file__, он используется как часть представления модуля.Если у модуля нет
__file__, но есть__loader__, не равныйNone, то в качестве части представления модуля используется представление загрузчика.В остальных случаях в представлении используется просто
__name__модуля.
Изменено в версии 3.12: Использование module_repr(), устаревшее с Python 3.4, удалено в Python 3.12, и этот метод больше не вызывается при определении представления модуля.
5.4.6. Инвалидация кешированного байт-кода¶
Прежде чем загрузить кешированный байт-код из файла .pyc, Python проверяет, актуален ли кеш по отношению к исходному файлу .py. По умолчанию Python делает это, сохраняя в файле кеша при его записи временную метку последнего изменения и размер исходного файла. Во время выполнения система импорта затем проверяет файл кеша, сравнивая сохранённые в нём метаданные с метаданными исходного файла.
Python также поддерживает файлы кеша «на основе хеша», в которых хранится хеш содержимого исходного файла, а не его метаданные. Существует два варианта файлов .pyc на основе хеша: проверяемые и непроверяемые. Для проверяемых файлов .pyc на основе хеша Python проверяет файл кеша, вычисляя хеш исходного файла и сравнивая полученный хеш с хешем в файле кеша. Если проверяемый файл кеша на основе хеша оказывается недействительным, Python пересоздаёт его и записывает новый проверяемый файл кеша на основе хеша. Для непроверяемых файлов .pyc на основе хеша Python просто считает файл кеша действительным, если он существует. Поведение при проверке файлов .pyc на основе хеша можно переопределить с помощью флага --check-hash-based-pycs.
Изменено в версии 3.7: Добавлены файлы .pyc на основе хеша. Ранее Python поддерживал только инвалидацию кешей байт-кода на основе временных меток.
5.5. Поисковик на основе пути¶
Как уже упоминалось, Python поставляется с несколькими поисковиками мета-пути по умолчанию. Один из них, называемый поисковиком на основе пути (PathFinder), выполняет поиск по пути импорта, содержащему список элементов пути. Каждый элемент пути указывает место для поиска модулей.
Сам по себе поисковик на основе пути не умеет ничего импортировать. Вместо этого он обходит отдельные элементы пути, сопоставляя каждому из них поисковик элемента пути, который умеет обрабатывать данный конкретный вид путей.
Набор поисковиков элементов пути по умолчанию реализует всю семантику поиска модулей в файловой системе и обрабатывает особые типы файлов, такие как исходный код Python (файлы .py), байт-код Python (файлы .pyc) и разделяемые библиотеки (например, файлы .so). Если это поддерживается модулем zipimport стандартной библиотеки, поисковики элементов пути по умолчанию также загружают все эти типы файлов (кроме разделяемых библиотек) из zip-файлов.
Within a single path entry, the default path entry finders check for a
regular package first, then for extension modules, then for source
files, and finally for bytecode files. For example, if the same directory
contains both spam/__init__.py and spam.py, import spam will
import the package from spam/__init__.py. A directory without an
__init__.py file is treated as a namespace package portion only if
no matching module is found. Note that this does not override the order of the
import path.
Элементы пути не обязаны ограничиваться местами в файловой системе. Они могут ссылаться на URL, запросы к базе данных или любое другое место, которое можно задать в виде строки.
Поисковик на основе пути предоставляет дополнительные обработчики и протоколы, чтобы вы могли расширять и настраивать типы элементов пути, по которым возможен поиск. Например, чтобы поддерживать элементы пути в виде сетевых URL, можно написать обработчик, реализующий семантику HTTP для поиска модулей в Интернете. Этот обработчик (вызываемый объект) возвращал бы поисковик элемента пути, который поддерживает описанный ниже протокол и используется для получения загрузчика модуля из Интернета.
Небольшое предостережение: в этом и предыдущем разделах используется термин поисковик, а различаются они с помощью терминов поисковик мета-пути и поисковик элемента пути. Эти два типа поисковиков очень похожи, поддерживают сходные протоколы и схожим образом работают в процессе импорта, но важно помнить, что между ними есть тонкие различия. В частности, поисковики мета-пути работают в начале процесса импорта и запускаются обходом sys.meta_path.
Напротив, поисковики элементов пути в некотором смысле — деталь реализации поисковика на основе пути: если удалить поисковик на основе пути из sys.meta_path, семантика поисковиков элементов пути вообще не будет задействована.
5.5.1. Поисковики элементов пути¶
Поисковик на основе пути отвечает за поиск и загрузку модулей и пакетов Python, расположение которых задаётся строковым элементом пути. Большинство элементов пути указывают места в файловой системе, но ими дело не ограничивается.
Будучи поисковиком мета-пути, поисковик на основе пути реализует описанный ранее протокол find_spec(), однако он предоставляет дополнительные обработчики, с помощью которых можно настраивать, как модули ищутся и загружаются по пути импорта.
Поисковик на основе пути использует три переменные: sys.path, sys.path_hooks и sys.path_importer_cache. Кроме того, используются атрибуты __path__ объектов пакетов. Всё это даёт дополнительные способы настройки механизма импорта.
sys.path содержит список строк, задающих места поиска модулей и пакетов. Он инициализируется из переменной окружения PYTHONPATH, а также из ряда других значений по умолчанию, зависящих от конкретной установки и реализации. Элементы sys.path могут указывать каталоги в файловой системе, zip-файлы и потенциально другие «места» (см. модуль site), в которых нужно искать модули, например URL или запросы к базе данных. В sys.path должны присутствовать только строки. Данные всех остальных типов игнорируются.
Поисковик на основе пути является поисковиком мета-пути, поэтому механизм импорта начинает поиск по пути импорта, вызывая метод find_spec() поисковика на основе пути, как описано ранее. Если аргумент path метода find_spec() задан, то это список строковых путей для обхода — обычно атрибут __path__ пакета при импорте внутри этого пакета. Если аргумент path равен None, это означает импорт верхнего уровня, и используется sys.path.
Поисковик на основе пути перебирает все элементы пути поиска и для каждого из них ищет подходящий поисковик элемента пути (PathEntryFinder). Поскольку эта операция может быть затратной (например, поиск может приводить к накладным расходам на вызовы stat()), поисковик на основе пути поддерживает кеш, сопоставляющий элементы пути с поисковиками элементов пути. Этот кеш хранится в sys.path_importer_cache (несмотря на название, в нём хранятся именно объекты-поисковики, а не только объекты импортёров). Таким образом, затратный поиск поисковика элемента пути для конкретного местоположения элемента пути нужно выполнить только один раз. Пользовательский код вправе удалять записи кеша из sys.path_importer_cache, заставляя поисковик на основе пути выполнить поиск по элементу пути заново.
Если элемента пути нет в кеше, поисковик на основе пути перебирает все вызываемые объекты в sys.path_hooks. Каждый из обработчиков элементов пути в этом списке вызывается с единственным аргументом — элементом пути, по которому нужно выполнить поиск. Этот вызываемый объект может либо вернуть поисковик элемента пути, способный обработать данный элемент пути, либо возбудить ImportError. Исключение ImportError используется поисковиком на основе пути как сигнал, что обработчик не может найти поисковик элемента пути для этого элемента пути. Это исключение игнорируется, и перебор пути импорта продолжается. Обработчик должен ожидать строку или объект bytes; кодировка объектов bytes остаётся на усмотрение обработчика (например, это может быть кодировка файловой системы, UTF-8 или что-то другое), и если обработчик не может декодировать аргумент, он должен возбудить ImportError.
Если перебор sys.path_hooks завершается, а поисковик элемента пути не возвращён, то метод find_spec() поисковика на основе пути сохранит None в sys.path_importer_cache (чтобы показать, что для этого элемента пути поисковика нет) и вернёт None, указывая, что этот поисковик мета-пути не смог найти модуль.
Если обработчик элемента пути из sys.path_hooks всё же возвращает поисковик элемента пути, то у этого поисковика запрашивается спецификация модуля, необходимая для его дальнейшей загрузки, по следующему протоколу.
Текущий рабочий каталог — он обозначается пустой строкой — обрабатывается несколько иначе, чем остальные элементы sys.path. Во-первых, если текущий рабочий каталог не удаётся определить или обнаруживается, что он не существует, в sys.path_importer_cache ничего не сохраняется. Во-вторых, значение для текущего рабочего каталога определяется заново при каждом поиске модуля. В-третьих, путь, который используется для sys.path_importer_cache и возвращается методом importlib.machinery.PathFinder.find_spec(), будет фактическим текущим рабочим каталогом, а не пустой строкой.
5.5.2. Протокол поисковика элемента пути¶
Чтобы поддерживать импорт модулей и инициализированных пакетов, а также вносить части в пакеты пространства имён, поисковики элементов пути должны реализовывать метод find_spec().
find_spec() принимает два аргумента: полное квалифицированное имя импортируемого модуля и (необязательный) целевой модуль. find_spec() возвращает полностью заполненную спецификацию модуля. В этой спецификации всегда будет задан «загрузчик» — за одним исключением.
Чтобы сообщить механизму импорта, что спецификация представляет часть пространства имён, поисковик элемента пути устанавливает submodule_search_locations в список, содержащий эту часть.
Изменено в версии 3.4: Метод find_spec() заменил find_loader() и find_module(), которые теперь объявлены устаревшими, но будут использоваться, если find_spec() не определён.
Более старые поисковики элементов пути могут вместо find_spec() реализовывать один из этих двух устаревших методов. Эти методы по-прежнему учитываются ради обратной совместимости. Однако если у поисковика элемента пути реализован find_spec(), устаревшие методы игнорируются.
find_loader() принимает один аргумент — полное квалифицированное имя импортируемого модуля. find_loader() возвращает кортеж из двух элементов: первый — загрузчик, второй — часть пространства имён.
Для обратной совместимости с другими реализациями протокола импорта многие поисковики элементов пути также поддерживают тот же традиционный метод find_module(), что и поисковики мета-пути. Однако методы find_module() поисковиков элементов пути никогда не вызываются с аргументом path (предполагается, что они запоминают нужную информацию о пути при первоначальном вызове обработчика пути).
Метод find_module() у поисковиков элементов пути объявлен устаревшим, поскольку он не позволяет поисковику элемента пути вносить части в пакеты пространства имён. Если у поисковика элемента пути есть и find_loader(), и find_module(), система импорта всегда вызывает find_loader(), а не find_module().
Изменено в версии 3.10: Вызовы find_module() и find_loader() системой импорта будут возбуждать ImportWarning.
Изменено в версии 3.12: Методы find_module() и find_loader() были удалены.
5.6. Замена стандартной системы импорта¶
Самый надёжный способ заменить всю систему импорта — удалить содержимое sys.meta_path по умолчанию и полностью заменить его пользовательским обработчиком мета-пути.
Если допустимо изменить поведение только инструкций import, не затрагивая другие API, обращающиеся к системе импорта, то может оказаться достаточно заменить встроенную функцию __import__().
Чтобы выборочно запретить импорт некоторых модулей из обработчика, стоящего в начале мета-пути (а не отключать стандартную систему импорта целиком), достаточно возбудить исключение ModuleNotFoundError прямо из find_spec(), вместо возврата None. Последнее означает, что поиск по мета-пути должен продолжаться, тогда как возбуждение исключения немедленно его прекращает.
5.7. Относительные импорты внутри пакета¶
Относительные импорты используют ведущие точки. Одна ведущая точка обозначает относительный импорт, начинающийся с текущего пакета. Две или более ведущих точек обозначают относительный импорт из родительских пакетов текущего пакета, по одному уровню на каждую точку после первой. Например, пусть дана следующая структура пакета:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
В файле subpackage1/moduleX.py или subpackage1/__init__.py допустимы следующие относительные импорты:
from .moduleY import spam
from .moduleY import spam as ham
from . import moduleY
from ..subpackage1 import moduleY
from ..subpackage2.moduleZ import eggs
from ..moduleA import foo
Абсолютные импорты могут использовать любую из форм import <> или from <> import <>, а относительные — только вторую. Причина в том, что инструкция:
import XXX.YYY.ZZZ
должна делать XXX.YYY.ZZZ пригодным для использования выражением, но .moduleY допустимым выражением не является.
5.8. Особенности модуля __main__¶
Модуль __main__ — особый случай по отношению к системе импорта Python. Как отмечено в другом месте, модуль __main__ инициализируется непосредственно при запуске интерпретатора, подобно sys и builtins. Однако, в отличие от этих двух, он не вполне подходит под определение встроенного модуля. Это связано с тем, что способ инициализации __main__ зависит от флагов и других параметров, с которыми запущен интерпретатор.
5.8.1. __main__.__spec__¶
В зависимости от способа инициализации __main__ атрибут __main__.__spec__ устанавливается соответствующим образом или в None.
Когда Python запускается с параметром -m, __spec__ устанавливается в спецификацию соответствующего модуля или пакета. __spec__ также заполняется, когда модуль __main__ загружается при выполнении каталога, zip-файла или другого элемента sys.path.
В остальных случаях __main__.__spec__ устанавливается в None, поскольку код, использованный для заполнения __main__, не соответствует непосредственно импортируемому модулю:
интерактивное приглашение
параметр
-cзапуск из стандартного ввода
запуск непосредственно из файла с исходным кодом или байт-кодом
Обратите внимание, что в последнем случае __main__.__spec__ всегда равен None, даже если файл технически можно было бы импортировать напрямую как модуль. Если в __main__ нужны корректные метаданные модуля, используйте параметр -m.
Заметьте также, что даже если __main__ соответствует импортируемому модулю и __main__.__spec__ установлен соответственно, они всё равно считаются разными модулями. Это связано с тем, что блоки, защищённые проверками if __name__ == "__main__":, выполняются только когда модуль используется для заполнения пространства имён __main__, а не при обычном импорте.
5.9. Ссылки¶
Механизм импорта значительно изменился со времён ранних версий Python. Исходная спецификация пакетов по-прежнему доступна для чтения, хотя некоторые детали изменились с момента написания этого документа.
Исходной спецификацией для sys.meta_path был PEP 302 с последующим расширением в PEP 420.
PEP 420 ввёл пакеты пространства имён в Python 3.3. PEP 420 также ввёл протокол find_loader() как альтернативу find_module().
PEP 366 описывает добавление атрибута __package__ для явных относительных импортов в главных модулях.
PEP 328 ввёл абсолютные и явные относительные импорты и изначально предлагал __name__ для семантики, которую PEP 366 в итоге определил для __package__.
PEP 338 определяет выполнение модулей как скриптов.
PEP 451 добавляет инкапсуляцию состояния импорта каждого модуля в объектах спецификации. Он также перекладывает большую часть шаблонных обязанностей загрузчиков обратно на механизм импорта. Эти изменения позволяют объявить устаревшими несколько API системы импорта, а также добавить новые методы в поисковики и загрузчики.
Примечания