Система импорта
Код 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.
5.2.2. Пакеты пространств имён
Пакет пространства имён — это объединение различных частей, каждая из которых добавляет вложенный пакет в родительский пакет. Части могут находиться в разных местах файловой системы. Их также можно найти в ZIP-файлах, в сети или в любом другом месте, которое Python проверяет при импорте. Пакеты пространств имён могут как соответствовать объектам файловой системы, так и не соответствовать им; они могут быть виртуальными модулями, не имеющими конкретного представления.
В пакетах пространств имён для атрибута __path__ не используется обычный список. Вместо этого применяется специальный итерируемый тип, который автоматически выполняет новый поиск частей пакета при следующей попытке импорта внутри этого пакета, если путь родительского пакета (или sys.path для пакета верхнего уровня) изменился.
У пакетов пространств имён нет файла parent/__init__.py. Более того, во время поиска при импорте могут быть найдены несколько каталогов parent, каждый из которых предоставлен отдельной частью пакета. Поэтому parent/one может физически находиться не рядом с parent/two. В таком случае Python создаёт пакет пространства имён для пакета верхнего уровня parent при импорте его самого или одного из его вложенных пакетов.
Спецификацию пакетов пространств имён см. также в 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'):
# It is assumed 'exec_module' will also be defined on the loader.
module = spec.loader.create_module(spec)
if module is None:
module = ModuleType(spec.name)
# The import-related module attributes get set here:
_init_module_attrs(spec, module)
if spec.loader is None:
# unsupported
raise ImportError
if spec.origin is None and spec.submodule_search_locations is not None:
# namespace package
sys.modules[spec.name] = module
elif not hasattr(spec.loader, 'exec_module'):
module = spec.loader.load_module(spec.name)
else:
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().
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: Метод 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: Если определён exec_module(), но не create_module(), вызывается предупреждение DeprecationWarning.
Изменено в версии 3.6: Если определён exec_module(), но не create_module(), вызывается исключение ImportError.
Изменено в версии 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-файлов.
Элементы пути не обязательно должны указывать только на расположения в файловой системе. Они могут ссылаться на 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() возвращает полностью заполненную спецификацию модуля. Эта спецификация всегда содержит значение для “loader” (за одним исключением).
Чтобы сообщить механизму импорта, что спецификация представляет собой часть пространства имён, поисковик элементов пути устанавливает submodule_search_locations в список, содержащий эту часть.
Изменено в версии 3.4: find_spec() заменил find_loader() и find_module(). Оба метода теперь устарели, но будут использоваться, если find_spec() не определён.
Старые поисковики элементов пути могут реализовывать один из этих двух устаревших методов вместо find_spec(). Эти методы по-прежнему поддерживаются для обратной совместимости. Однако если в поисковике элементов пути реализован find_spec(), устаревшие методы игнорируются.
find_loader() принимает один аргумент — полное имя импортируемого модуля. find_loader() возвращает 2-кортеж, первый элемент которого — загрузчик, а второй — часть пространства имён.
Для обратной совместимости с другими реализациями протокола импорта многие поисковики элементов пути также поддерживают тот же традиционный метод 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 и полностью заменить его пользовательским перехватчиком метапути.
Если допустимо изменить только поведение инструкций импорта, не затрагивая другие 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 системы импорта, а также добавить новые методы для поисковиков и загрузчиков.
Сноски
© 2001–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/reference/import.html