Документация Python неофициальный перевод
Содержание страницы

5. Система импортаThe import system

Код 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. ПакетыPackages

Python has only one type of module object, and all modules are of this type, regardless of whether the module is implemented in Python, C, or something else. To help organize modules and provide a naming hierarchy, Python has a concept of packages.

Можно представлять пакеты как каталоги в файловой системе, а модули – как файлы в этих каталогах, но не стоит воспринимать эту аналогию слишком буквально, поскольку пакеты и модули не обязательно происходят из файловой системы. В целях данной документации мы будем использовать эту удобную аналогию каталогов и файлов. Как и каталоги файловой системы, пакеты организованы иерархически, и пакеты могут содержать вложенные пакеты, а также обычные модули.

Важно помнить, что все пакеты являются модулями, но не все модули являются пакетами. Или, иными словами, пакеты – это просто особый вид модуля. В частности, любой модуль, содержащий атрибут __path__, считается пакетом.

У всех модулей есть имя. Имена подпакетов отделяются от имени родительского пакета точкой, подобно тому, как осуществляется доступ к атрибутам в Python. Таким образом, может существовать модуль с именем sys и пакет с именем email, который, в свою очередь, содержит подпакет email.mime и модуль внутри этого подпакета с именем email.mime.text.

5.2.1. Обычные пакетыRegular packages

Python defines two types of packages, regular packages and namespace packages. Regular packages are traditional packages as they existed in Python 3.2 and earlier. A regular package is typically implemented as a directory containing an __init__.py file. When a regular package is imported, this __init__.py file is implicitly executed, and the objects it defines are bound to names in the package’s namespace. The __init__.py file can contain the same Python code that any other module can contain, and Python will add some additional attributes to the module when it is imported.

Например, следующая структура файловой системы определяет пакет верхнего уровня 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. Пакеты пространств именNamespace packages

A namespace package is a composite of various portions, where each portion contributes a subpackage to the parent package. Portions may reside in different locations on the file system. Portions may also be found in zip files, on the network, or anywhere else that Python searches during import. Namespace packages may or may not correspond directly to objects on the file system; they may be virtual modules that have no concrete representation.

Пакеты пространств имен не используют обычный список для своего атрибута __path__. Вместо этого они используют пользовательский итерируемый тип, который автоматически выполняет новый поиск частей пакета при следующей попытке импорта в рамках этого пакета, если путь родительского пакета (или sys.path для пакета верхнего уровня) изменяется.

В пакетах пространств имен нет файла parent/__init__.py. Фактически, при поиске импорта может быть найдено несколько каталогов parent, каждый из которых предоставляется разной частью. Таким образом, parent/one может физически не находиться рядом с parent/two. В этом случае Python создаст пакет пространства имен для пакета верхнего уровня parent всякий раз, когда импортируется сам этот пакет или любой из его вложенных пакетов.

See also PEP 420 for the namespace package specification.

5.3. ПоискSearching

To begin the search, Python needs the fully qualified name of the module (or package, but for the purposes of this discussion, the difference is immaterial) being imported. This name may come from various arguments to the import statement, or from the parameters to the importlib.import_module() or __import__() functions.

Это имя будет использоваться на различных этапах поиска импорта и может быть точечным путём к подмодулю, например foo.bar.baz. В этом случае Python сначала пытается импортировать foo, затем foo.bar и, наконец, foo.bar.baz. Если какой-либо из промежуточных импортов завершается неудачей, возбуждается ModuleNotFoundError.

5.3.1. Кеш модулейThe module cache

Первое место, проверяемое при поиске импорта, – это 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. Искатели и загрузчикиFinders and loaders

Если именованный модуль не найден в sys.modules, то вызывается протокол импорта Python для поиска и загрузки модуля. Этот протокол состоит из двух концептуальных объектов: искателей и загрузчиков. Задача искателя – определить, может ли он найти именованный модуль, используя любую известную ему стратегию. Объекты, реализующие оба этих интерфейса, называются импортерами – они возвращают самих себя, когда обнаруживают, что могут загрузить запрошенный модуль.

Python includes a number of default finders and importers. The first one knows how to locate built-in modules, and the second knows how to locate frozen modules. A third default finder searches an import path for modules. The import path is a list of locations that may name file system paths or zip files. It can also be extended to search for any locatable resource, such as those identified by URLs.

Механизм импорта является расширяемым, поэтому можно добавлять новые искатели для расширения диапазона и области поиска модулей.

Искатели на самом деле не загружают модули. Если они могут найти именованный модуль, они возвращают спецификацию модуля – инкапсуляцию информации, связанной с импортом модуля, которую затем использует механизм импорта при загрузке модуля.

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

Изменено в версии 3.4: В предыдущих версиях Python искатели возвращали загрузчики напрямую, тогда как теперь они возвращают спецификации модулей, которые содержат загрузчики. Загрузчики по-прежнему используются при импорте, но имеют меньше обязанностей.

5.3.3. Хуки импортаImport hooks

Механизм импорта разработан как расширяемый; основным механизмом для этого являются хуки импорта. Существует два типа хуков импорта: мета-хуки и хуки пути импорта.

Мета-хуки вызываются в начале обработки импорта, до того, как произойдет любая другая обработка импорта, кроме поиска в кеше sys.modules. Это позволяет мета-хукам переопределять обработку sys.path, замороженных модулей или даже встроенных модулей. Мета-хуки регистрируются путем добавления новых объектов искателей в sys.meta_path, как описано ниже.

Хуки пути импорта вызываются как часть обработки sys.path (или package.__path__) в тот момент, когда встречается связанный с ними элемент пути. Хуки пути импорта регистрируются путем добавления новых вызываемых объектов в sys.path_hooks, как описано ниже.

5.3.4. Мета-путьThe meta path

Когда именованный модуль не найден в 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().

5.4. ЗагрузкаLoading

Если (и когда) спецификация модуля найдена, механизм импорта использует её (и содержащийся в ней загрузчик) при загрузке модуля. Ниже приведено приблизительное описание того, что происходит во время части импорта, связанной с загрузкой:

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
if spec.origin is None and spec.submodule_search_locations is not None:
    # пакет пространства имён
    sys.modules[spec.name] = module
elif not hasattr(spec.loader, 'exec_module'):
    module = spec.loader.load_module(spec.name)
    # Устанавливает __loader__ и __package__, если они отсутствуют.
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. ЗагрузчикиLoaders

Загрузчики модулей выполняют критическую функцию загрузки: выполнение модуля. Механизм импорта вызывает метод 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: Исключение DeprecationWarning возбуждается, когда exec_module() определён, а create_module() – нет.

Изменено в версии 3.6: Исключение ImportError возбуждается, когда exec_module() определён, а create_module() – нет.

5.4.2. ПодмодулиSubmodules

Когда подмодуль загружается с помощью любого механизма (например, API importlib, операторов import или import-from, или встроенной __import__()), в пространстве имён родительского модуля создаётся связывание с объектом подмодуля. Например, если пакет spam содержит подмодуль foo, то после импорта spam.foo у spam будет атрибут foo, который связан с подмодулем. Допустим, имеется следующая структура каталогов:

spam/
    __init__.py
    foo.py
    bar.py

и spam/__init__.py содержит следующие строки:

from .foo import Foo
from .bar import Bar

тогда выполнение следующего кода создаёт привязку имени к foo и bar в spam модуле:

>>> import spam
>>> spam.foo
<module 'spam.foo' from '/tmp/imports/spam/foo.py'>
>>> spam.bar
<module 'spam.bar' from '/tmp/imports/spam/bar.py'>

Учитывая привычные правила связывания имён в Python, это может показаться удивительным, но на самом деле это фундаментальная особенность системы импорта. Сохраняется следующий инвариант: если имеются sys.modules['spam'] и sys.modules['spam.foo'] (как после импорта выше), то второй должен быть атрибутом foo первого.

5.4.3. Спецификация модуляModule spec

Механизм импорта использует различную информацию о каждом модуле во время импорта, особенно перед загрузкой. Большая часть этой информации является общей для всех модулей. Назначение спецификации модуля – инкапсулировать эту связанную с импортом информацию для каждого модуля отдельно.

Использование спецификации во время импорта позволяет передавать состояние между компонентами системы импорта, например, между искателем, который создаёт спецификацию модуля, и загрузчиком, который её выполняет. Самое главное, это позволяет механизму импорта выполнять стандартные операции загрузки, тогда как без спецификации модуля эта ответственность лежала на загрузчике.

Спецификация модуля доступна как атрибут __spec__ объекта модуля. Подробнее о содержимом спецификации модуля см. в ModuleSpec.

Новое в версии 3.4.

5.4.5. module.__path__

По определению, если у модуля есть атрибут __path__, то это пакет.

Атрибут __path__ пакета используется при импорте его подпакетов. В механизме импорта он работает почти так же, как sys.path, то есть предоставляет список мест для поиска модулей во время импорта. Однако __path__ обычно гораздо более ограничен, чем sys.path.

__path__ должен быть итерируемым объектом строк, но может быть пустым. Те же правила, что и для sys.path, применяются к __path__ пакета, а sys.path_hooks (описанные ниже) используются при обходе __path__ пакета.

Файл __init__.py пакета может задавать или изменять атрибут __path__ пакета, и это был обычный способ реализации пространств имён пакетов до PEP 420. С принятием PEP 420 пространства имён пакетов больше не нуждаются в __init__.py файлах, содержащих только код манипуляции __path__; механизм импорта автоматически устанавливает __path__ корректно для пространства имён пакетов.

5.4.6. Строковые представления модулейModule reprs

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

Если у модуля есть спецификация (__spec__), механизм импорта попытается сгенерировать строковое представление на её основе. Если это не удаётся или спецификация отсутствует, система импорта создаст строковое представление по умолчанию, используя любую доступную информацию о модуле. Она попытается использовать module.__name__, module.__file__ и module.__loader__ в качестве входных данных для строкового представления, с умолчаниями для отсутствующей информации.

Ниже приведены точные правила:

  • Если у модуля есть атрибут __spec__, для генерации строкового представления используется информация из спецификации. Учитываются атрибуты «name», «loader», «origin» и «has_location».

  • Если у модуля есть атрибут __file__, он используется как часть строкового представления модуля.

  • Если у модуля нет __file__, но есть __loader__, который не равен None, то строковое представление загрузчика используется как часть строкового представления модуля.

  • В противном случае в строковом представлении используется просто __name__ модуля.

Изменено в версии 3.4: Использование loader.module_repr() устарело, и теперь механизм импорта использует спецификацию модуля для генерации представления модуля.

Для обратной совместимости с Python 3.3 представление модуля будет генерироваться вызовом метода module_repr() загрузчика, если он определён, до попытки использования любого из описанных выше подходов. Однако этот метод устарел.

5.4.7. Инвалидация кэшированного байт-кодаCached bytecode invalidation

Прежде чем Python загрузит кэшированный байт-код из файла .pyc, он проверяет, актуален ли кэш относительно исходного файла .py. По умолчанию Python делает это, сохраняя метку времени последнего изменения и размер исходного файла в кэш-файле при его записи. Во время выполнения система импорта затем проверяет кэш-файл, сверяя сохранённые метаданные в кэш-файле с метаданными исходного файла.

Python также поддерживает кэш-файлы на основе хеша, в которых вместо метаданных хранится хеш содержимого исходного файла. Существует два варианта таких .pyc файлов: проверяемые и непроверяемые. Для проверяемых .pyc файлов Python проверяет кэш-файл, хешируя исходный файл и сравнивая полученный хеш с хешем в кэш-файле. Если проверяемый кэш-файл на основе хеша оказывается недействительным, Python пересоздаёт его и записывает новый проверяемый кэш-файл на основе хеша. Для непроверяемых .pyc файлов Python просто считает кэш-файл действительным, если он существует. Поведение проверки .pyc файлов на основе хеша можно переопределить с помощью флага --check-hash-based-pycs.

Изменено в версии 3.7: Добавлены .pyc файлы на основе хеша. Ранее Python поддерживал только инвалидацию кэшей байт-кода на основе временных меток.

5.5. Искатель на основе путейThe Path Based Finder

Как упоминалось ранее, Python включает несколько искателей мета-путей по умолчанию. Один из них, называемый искатель на основе путей (PathFinder), ищет в пути импорта, который содержит список записей пути. Каждая запись пути указывает расположение для поиска модулей.

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

Набор искателей записей пути по умолчанию реализует всю семантику поиска модулей в файловой системе, обрабатывая специальные типы файлов, такие как исходный код Python (файлы .py), байт-код Python (файлы .pyc) и разделяемые библиотеки (например, файлы .so). Если это поддерживается модулем zipimport из стандартной библиотеки, искатели записей пути по умолчанию также загружают все эти типы файлов (кроме разделяемых библиотек) из zip-файлов.

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

Искатель на основе путей предоставляет дополнительные перехватчики и протоколы, позволяющие расширять и настраивать типы искомых записей пути. Например, если требуется поддерживать записи пути в виде сетевых URL, можно написать перехватчик, реализующий семантику HTTP для поиска модулей в вебе. Этот перехватчик (вызываемый объект) вернёт искатель записей пути, поддерживающий описанный ниже протокол, который затем будет использован для получения загрузчика модуля из веба.

Предупреждение: в этом и предыдущем разделах используется термин finder (искатель), различая их с помощью терминов искатель мета-путей и искатель записей пути. Эти два типа искателей очень похожи, поддерживают схожие протоколы и действуют схожим образом в процессе импорта, однако важно помнить, что между ними есть тонкие различия. В частности, искатели мета-путей работают в начале процесса импорта, начиная с обхода sys.meta_path.

Напротив, искатели записей пути в некотором смысле являются деталью реализации искателя на основе путей, и на самом деле, если бы искатель на основе путей был удалён из sys.meta_path, ни одна из семантик искателя записей пути не была бы задействована.

5.5.1. Искатели записей путиPath entry finders

Искатель на основе путей отвечает за поиск и загрузку модулей и пакетов 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, заставляя путевой искатель снова выполнять поиск элемента пути 3.

If the path entry is not present in the cache, the path based finder iterates over every callable in sys.path_hooks. Each of the path entry hooks in this list is called with a single argument, the path entry to be searched. This callable may either return a path entry finder that can handle the path entry, or it may raise ImportError. An ImportError is used by the path based finder to signal that the hook cannot find a path entry finder for that path entry. The exception is ignored and import path iteration continues. The hook should expect either a string or bytes object; the encoding of bytes objects is up to the hook (e.g. it may be a file system encoding, UTF-8, or something else), and if the hook cannot decode the argument, it should raise ImportError.

If sys.path_hooks iteration ends with no path entry finder being returned, then the path based finder’s find_spec() method will store None in sys.path_importer_cache (to indicate that there is no finder for this path entry) and return None, indicating that this meta path finder could not find the module.

If a path entry finder is returned by one of the path entry hook callables on sys.path_hooks, then the following protocol is used to ask the finder for a module spec, which is then used when loading the module.

Текущий рабочий каталог – обозначаемый пустой строкой – обрабатывается несколько иначе, чем другие записи в sys.path. Во-первых, если текущий рабочий каталог не существует, в sys.path_importer_cache не сохраняется никакого значения. Во-вторых, значение текущего рабочего каталога запрашивается заново при каждом поиске модуля. В-третьих, путь, используемый для sys.path_importer_cache и возвращаемый importlib.machinery.PathFinder.find_spec(), будет фактическим текущим рабочим каталогом, а не пустой строкой.

5.5.2. Протокол искателя записей путиPath entry finder protocol

Для поддержки импорта модулей и инициализированных пакетов, а также для внесения частей в пространственно-именованные пакеты, искатели записей пути должны реализовывать метод 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() takes one argument, the fully qualified name of the module being imported. find_loader() returns a 2-tuple where the first item is the loader and the second item is a namespace portion.

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

Метод find_module() у искателей записей пути устарел, так как он не позволяет искателю записей пути вносить части в пространственно-именованные пакеты. Если на искателе записей пути существуют и find_loader(), и find_module(), система импорта всегда будет вызывать find_loader() в предпочтение find_module().

5.6. Замена стандартной системы импортаReplacing the standard import system

Самый надёжный механизм замены всей системы импорта – удалить содержимое по умолчанию из sys.meta_path, полностью заменив их пользовательским хуком метапути.

Если достаточно изменить поведение только инструкций import, не затрагивая другие API, которые обращаются к системе импорта, то может быть достаточно замены встроенной функции __import__(). Этот метод также можно применить на уровне модуля, чтобы изменить поведение инструкций import только в этом модуле.

Чтобы выборочно предотвратить импорт некоторых модулей из хука на раннем этапе метапути (вместо полного отключения стандартной системы импорта), достаточно возбудить ModuleNotFoundError непосредственно из find_spec(), а не возвращать None. Последнее означает, что поиск по метапути должен продолжаться, тогда как возбуждение исключения немедленно его прекращает.

5.7. Относительный импорт в пакетахPackage Relative Imports

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

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__Special considerations for __main__

The __main__ module is a special case relative to Python’s import system. As noted elsewhere, the __main__ module is directly initialized at interpreter startup, much like sys and builtins. However, unlike those two, it doesn’t strictly qualify as a built-in module. This is because the manner in which __main__ is initialized depends on the flags and other options with which the interpreter is invoked.

5.8.1. __main__.__spec__

В зависимости от того, как инициализируется __main__, __main__.__spec__ устанавливается соответствующим образом или в None.

Когда Python запускается с опцией -m, __spec__ устанавливается в спецификацию модуля соответствующего модуля или пакета. __spec__ также заполняется, когда модуль __main__ загружается при выполнении каталога, zip-файла или другой записи sys.path.

In the remaining cases __main__.__spec__ is set to None, as the code used to populate the __main__ does not correspond directly with an importable module:

  • интерактивная подсказка

  • -c опция

  • запуск из stdin

  • запуск напрямую из исходного или байт-код файла

Обратите внимание, что __main__.__spec__ всегда None в последнем случае, даже если файл технически можно импортировать напрямую как модуль. Используйте флаг -m, если нужны корректные метаданные модуля в __main__.

Также обратите внимание, что даже если __main__ соответствует импортируемому модулю и __main__.__spec__ установлено соответствующим образом, они всё равно считаются отдельными модулями. Это связано с тем, что блоки, защищённые проверками if __name__ == "__main__":, выполняются только тогда, когда модуль используется для заполнения пространства имён __main__, а не при обычном импорте.

5.9. Открытые вопросыOpen issues

XXX Очень хотелось бы иметь диаграмму.

XXX * (import_machinery.rst) как насчёт раздела, посвящённого только атрибутам модулей и пакетов, возможно, расширяющего или заменяющего соответствующие записи на странице справочника по модели данных?

XXX runpy, pkgutil и т.п. в библиотечном руководстве должны получить ссылки «См. также» в начале, указывающие на новый раздел о системе импорта.

XXX Добавить больше пояснений о различных способах инициализации __main__?

XXX Добавить больше информации о __main__ особенностях/подводных камнях (например, скопировать из PEP 395).

5.10. СсылкиReferences

Механизм импорта значительно изменился с первых дней 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 в системе импорта, а также добавление новых методов для искателей и загрузчиков.

Сноски

1

См. types.ModuleType.

2

Реализация importlib избегает прямого использования возвращаемого значения. Вместо этого она получает объект модуля, выполняя поиск имени модуля в sys.modules. Косвенный эффект этого заключается в том, что импортированный модуль может заменить себя в sys.modules. Это поведение, зависящее от реализации, которое не гарантируется в других реализациях Python.

3

В устаревшем коде можно встретить экземпляры imp.NullImporter в sys.path_importer_cache. Рекомендуется изменить код на использование None. Подробнее см. Porting Python code.