Система импорта
Код 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. Таким образом, у вас может быть модуль под названием sys и пакет под названием 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().
5.4. Загрузка
Если и когда спецификация модуля (module spec) найдена, механизм импорта использует её (и загрузчик, который она содержит) при загрузке модуля. Вот приблизительное описание того, что происходит во время процесса загрузки импорта:
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)
# Set __loader__ and __package__ if missing.
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: Исключение DeprecationWarning генерируется, когда exec_module() определён, но create_module() нет.
Изменено в версии 3.6: Исключение ImportError генерируется, когда exec_module() определён, но create_module() нет.
5.4.2. Подмодули
Когда подмодуль загружается с помощью любого механизма (например, importlib APIs, операторов 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. Спецификация модуля
Механизм импорта использует различные сведения о каждом модуле во время импорта, особенно перед загрузкой. Большая часть информации является общей для всех модулей. Спецификация модуля предназначена для инкапсуляции этой информации, относящейся к импорту, на уровне каждого модуля.
Использование спецификации во время импорта позволяет передавать состояние между компонентами системы импорта, например, между поисковиком, который создаёт спецификацию модуля, и загрузчиком, который выполняет его. Наиболее важно то, что это позволяет механизму импорта выполнять вспомогательные операции загрузки, в то время как без спецификации модуля загрузчик брал на себя эту ответственность.
Спецификация модуля отображается как атрибут __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. Представления модулей
По умолчанию все модули имеют используемое представление, но в зависимости от установленных атрибутов и спецификации модуля вы можете более явно контролировать представление объектов модуля.
Если у модуля есть спецификация (__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. Проверка кэша байткода
Перед загрузкой Python кэшированного байткода из файла .pyc, он проверяет, является ли кэш актуальным относительно исходного файла .py. По умолчанию Python делает это, сохраняя отметку о последнем изменении и размер исходного файла в файле кэша при его записи. При запуске система импорта затем проверяет файл кэша, сравнивая сохранённые метаданные в файле кэша с метаданными исходного файла.
Python также поддерживает файлы кэша с хешем, которые сохраняют хеш содержимого исходного файла, а не его метаданные. Существуют два варианта хеш-файлов кэша: проверенные и непроверенные. Для проверенных хеш-файлов кэша Python проверяет файл кэша, генерируя хеш исходного файла и сравнивая его с хешем в файле кэша. Если обнаружен невалидный проверенный хеш-файл кэша, Python пересоздаёт его и записывает новый проверенный хеш-файл кэша. Для непроверенных хеш-файлов кэша Python просто предполагает, что файл кэша валиден, если он существует. Поведение проверки хеш-файлов кэша может быть переопределено флагом --check-hash-based-pycs.
Изменено в версии 3.7: Добавлены хеш-файлы кэша. Раньше 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, заставляя поисковик, основанный на пути, снова выполнить поиск записи пути 3.
Если запись пути отсутствует в кэше, поисковик, основанный на пути, итерирует по каждому вызываемому объекту в sys.path_hooks. Каждый из крючков записи пути в этом списке вызывается с одним аргументом — записью пути, которую нужно искать. Этот вызываемый объект может либо вернуть поисковик записи пути, который может обработать запись пути, либо вызвать ImportError. ImportError используется поисковиком, основанным на пути, для сигнализации о том, что крючок не может найти поисковик записи пути для этой записи пути. Исключение игнорируется, и итерация по пути импорта продолжается. Крючок должен ожидать строку или байтовый объект; кодировка байтовых объектов зависит от крючка (например, это может быть кодировка файловой системы, 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().
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 в последнем случае, даже если файл мог бы технически быть импортирован напрямую как модуль вместо этого. Используйте переключатель -m, если требуются корректные метаданные модуля в __main__.
Также обратите внимание, что даже когда __main__ соответствует импортируемому модулю и __main__.__spec__ устанавливается соответствующим образом, они по-прежнему считаются разными модулями. Это связано с тем, что блоки, защищенные проверками if __name__ == "__main__":, выполняются только тогда, когда модуль используется для заполнения пространства имён __main__, а не во время обычного импорта.
5.9. Открытые вопросы
XXX Было бы неплохо иметь диаграмму.
XXX * (import_machinery.rst) Как насчёт раздела, посвящённого только атрибутам модулей и пакетов, возможно, расширяющего или заменяющего соответствующие записи в справочнике по модели данных?
XXX runpy, pkgutil и т.д. в руководстве по библиотеке должны иметь ссылки «См. также» вверху, указывающие на новый раздел по системе импорта.
XXX Добавить больше объяснений, касающихся разных способов инициализации __main__?
XXX Добавить больше информации о нюансах/ошибках __main__ (т.е. скопировать из PEP 395).
5.10. Справочные материалы
Механизм импорта значительно эволюционировал со времен ранних версий 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вместо этого. Подробнее см. Перенос кода Python.
© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/reference/import.html