5. Система импорта¶
Python код в одном модуле получает доступ к коду в другом модуле
в процессе его импорта. Оператор import
—
это наиболее распространенный способ вызова механизма импорта, но не
единственный способ. Такие функции, как importlib.import_module()
и
встроенная __import__()
, также могут использоваться для вызова
механизма импорта.
Оператор import
объединяет две операции: он ищет именованный модуль, а затем
привязывает результаты этого поиска к имени в локальной области. Операция поиска
оператора import
определяется как вызов функции __import__()
с
соответствующими аргументами. Возвращаемое значение __import__()
используется для
выполнения операции привязки имени оператора import
. См. оператор
import
для получения точных сведений о операции привязки имени.
Прямой вызов __import__()
выполняет только поиск модуля и, если он найден,
операцию создания модуля. Хотя могут возникать определенные побочные эффекты,
такие как импорт родительских пакетов и обновление различных кешей (включая
sys.modules
). Только оператор import
выполняет операцию привязки имени.
Когда выполняется инструкция import
, вызывается стандартная встроенная
функция __import__()
. Другие механизмы для вызова системы импорта (например,
importlib.import_module()
) могут выбрать обход __import__()
и
использовать свои собственные решения для реализации семантики импорта.
Когда модуль импортируется впервые, Python ищет модуль и, если он найден,
создает объект модуля [1], инициализируя его. Если заданный модуль не
найден, поднимается ModuleNotFoundError
. Python реализует
различные стратегии поиска заданного модуля при вызове механизма импорта.
Эти стратегии могут быть изменены и расширены с помощью различных хуков,
описанных в следующих разделах.
Изменено в версии 3.3: Система импорта была обновлена, чтобы полностью реализовать вторую фазу
PEP 302. Больше нет никакого механизма неявного импорта — полная система
импорта представлена через sys.meta_path
. Кроме того, была
реализована поддержка пакетов собственного пространства имён (см. PEP 420).
5.1. importlib
¶
Модуль importlib
предоставляет богатый API для взаимодействия с системой
импорта. Например, importlib.import_module()
предоставляет рекомендуемый и
более простое API, чем встроенная __import__()
, для вызова механизма
импорта. Дополнительные сведения см. в документации библиотеки importlib
.
5.2. Пакеты¶
В Python есть только один тип объекта модуля, и все модули относятся к этому типу, независимо от того, реализован ли модуль на Python, C или чем-то ещё. Чтобы помочь организовать модули и обеспечить иерархию имён, в Python есть концепция пакетов.
Вы можете думать о пакетах как о каталогах в файловой системе, а о модулях как о файлах в каталогах, но не принимайте эту аналогию слишком буквально, поскольку пакеты и модули не обязательно должны происходить из файловой системы. Для целей этой документации мы будем использовать эту удобную аналогию каталогов и файлов. Подобно каталогам файловой системы, пакеты организованы иерархически, и сами пакеты могут содержать подпакеты, а также обычные модули.
Важно помнить, что все пакеты являются модулями, но не все модули являются
пакетами. Или другими словами, пакеты — это просто особый вид модуля. В
частности, пакетом считается любой модуль, содержащий атрибут __path__
.
У всех модулей есть имя. Имена подпакетов отделяются от имени родительского
пакета точками, как в стандартном синтаксисе доступа к атрибутам Python. Таким
образом, у вас может быть модуль с именем 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
.
По умолчанию в Python sys.meta_path
есть три средства поиска мета-пути: один знает,
как импортировать встроенные модули, второй знает, как импортировать
замороженные модули, а другой знает, как импортировать модули из путей импорта (то
есть поисковик на основе пути).
Изменено в версии 3.4: Метод поиска мета-пути find_spec()
заменил
устаревший find_module()
.
Хотя он будет продолжать работать без изменений, механизм импорта попробует его,
только если поисковик не реализует find_spec()
.
5.4. Загрузка¶
Если и когда будет найдена спецификация модуля, механизм импорта будет использовать её (и содержащийся в ней загрузчик) при загрузке модуля. Вот примерное представление о том, что происходит во время загрузки при импорте
module = None
if spec.loader is not None and hasattr(spec.loader, 'create_module'):
# Предполагается, что exec_module также будет определен в загрузчике.
module = spec.loader.create_module(spec)
if module is None:
module = ModuleType(spec.name)
# Здесь устанавливаются атрибуты модуля, связанные с импортом:
_init_module_attrs(spec, module)
if spec.loader is None:
# не поддерживается
raise ImportError
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. Загрузчики¶
Загрузчики модулей обеспечивают важнейшую функцию загрузки: выполнение модуля.
Механизм импорта вызывает метод 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. Подмодули¶
Когда подмодуль загружается с использованием любого механизма (например, 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. Спецификация модуля¶
Механизм импорта использует различную информацию о каждом модуле во время импорта, особенно перед загрузкой. Большая часть информации является общей для всех модулей. Цель спецификации модуля — инкапсулировать эту информацию, связанную с импортом каждого модуля.
Использование спецификации во время импорта позволяет передавать состояние между компонентами системы импорта, например между поисковиком, который создаёт спецификацию модуля, и загрузчиком, который её выполняет. Что наиболее важно, это позволяет машинерии импорта выполнять стандартные операции загрузки, тогда как без спецификации модуля эту ответственность несёт загрузчик.
Спецификация модуля представлена как атрибут __spec__
в объекте модуля. См.
ModuleSpec
для получения подробной информации
о содержании спецификации модуля.
Добавлено в версии 3.4.
5.4.4. Атрибуты модуля, связанные с импортом¶
Механизм импорта заполняет эти атрибуты для каждого объекта модуля во время загрузки в соответствии со спецификацией модуля, прежде чем загрузчик выполнит модуль.
-
__name__
¶ Атрибут
__name__
должен быть установлен на полное имя модуля. Это имя используется для уникальной идентификации модуля в системе импорта.
-
__loader__
¶ Атрибут
__loader__
должен быть установлен для объекта загрузчика, который механизм импорта использовал при загрузке модуля. Это в основном для самоанализа, но может использоваться для дополнительных функций, связанных с загрузчиком, например для получения данных связанных с загрузчиком.
-
__package__
¶ Должен быть установлен атрибут модуля
__package__
. Его значение должно быть строкой, но может быть таким же, как его__name__
. Когда модуль является пакетом, его значение__package__
должно быть установлено равным__name__
. Если модуль не является пакетом, в__package__
должна быть установлена пустая строка для модулей верхнего уровня или для подмодулей — имя родительского пакета. См. PEP 366 для получения дополнительной информации.Атрибут используется вместо
__name__
для вычисления явного относительного импорта для основных модулей, как определено в PEP 366. Ожидается, что он будет иметь то же значение, что и__spec__.parent
.Изменено в версии 3.6: Ожидается, что значение
__package__
будет таким же, как__spec__.parent
.
-
__spec__
¶ Атрибут
__spec__
должен быть установлен в соответствии со спецификацией модуля, которая использовалась при импорте модуля. Установка__spec__
в равной степени применима и к модулям инициализации при запуске интерпретатора. Единственное исключение —__main__
, где__spec__
- в некоторых случаях установит значение None.Если
__package__
не определён,__spec__.parent
используется в качестве запасного варианта.Добавлено в версии 3.4.
Изменено в версии 3.6:
__spec__.parent
используется как запасной вариант, когда__package__
не определён.
-
__path__
¶ Если модуль является пакетом (обычным или пространства имён), необходимо установить атрибут
__path__
объекта модуля. Значение должно быть итерируемым, но может быть пустым, если__path__
больше не имеет значения. Если__path__
не пуст, он должен создавать строки при итерировании. Более подробная информация о семантике__path__
приведена ниже.Модули, не входящие в пакет, не должны содержать атрибут
__path__
.
-
__file__
¶
-
__cached__
¶ __file__
не является обязательным. Если установлено, значение этого атрибута должно быть строкой. Система импорта может оставить__file__
не установленным, если он не имеет семантического значения (например, модуль, загруженный из базы данных).Если установлен
__file__
, также может быть целесообразно установить атрибут__cached__
, который является путём к любой скомпилированной версии кода (например, к байтовому скомпилированному файлу). Для установки этого атрибута необязательно, чтобы файл существовал; путь может просто указывать на место, где должен существовать скомпилированный файл (см. PEP 3147).Также целесообразно установить
__cached__
, когда__file__
не установлен. Однако этот сценарий довольно нетипичен. В конечном счете, загрузчик — это то, что использует__file__
и/или__cached__
. Поэтому, если загрузчик может загружаться из кэшированного модуля, но в остальном не загружается из файла, этот нетипичный сценарий может быть подходящим.
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. Reprs модуля¶
По умолчанию все модули содержат repr, однако в зависимости от атрибутов, установленных выше, и в спецификации модуля вы можете более явно управлять repr объектов модуля.
Если у модуля есть спецификация (__spec__
), механизм импорта попытается
сгенерировать из него repr. Если это не удается или спецификации отсутствуют,
система импорта создаст repr по умолчанию, используя любую информацию,
доступную в модуле. Она попытается использовать module.__name__
, module.__file__
и
module.__loader__
в качестве входных данных для repr со значениями по
умолчанию для любой информации, которая отсутствует.
Вот точные используемые правила:
- Если у модуля есть атрибут
__spec__
, информация в спецификации используется для генерации repr. Обращаются к атрибутам «name», «loader», «origin» и «has_location».- Если у модуля есть атрибут
__file__
, он используется как часть repr модуля.- Если в модуле нет
__file__
, но есть__loader__
, отличный отNone
, то repr загрузчика используется как часть repr модуля.- В противном случае просто используйте модуль
__name__
в repr.
Изменено в версии 3.4: Использование loader.module_repr()
устарело, и теперь спецификация модуля используется
механизмом импорта для генерации repr модуля.
Для обратной совместимости с Python 3.3 repr модуля будет сгенерирована
путём вызова метода загрузчика module_repr()
,
если он определен, перед попыткой любого из описанных выше подходов.
Однако этот метод устарел.
5.4.7. Инвалидация кэша байткода¶
Прежде чем Python загрузит кэшированный байт-код из файла .pyc
, он
проверяет, обновлен ли кеш с исходным файлом .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
, заставляя средство поиска на основе пути
снова выполнить поиск записи пути [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 * (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 . См. Porting Python code для получения более
подробной информации. |