Система импорта
Код Python в одном модуле получает доступ к коду другого модуля посредством импорта этого модуля. Оператор import — наиболее распространённый способ вызова механизма импорта, но не единственный. Для вызова механизма импорта также можно использовать такие функции, как importlib.import_module(), и встроенную функцию __import__().
Оператор import объединяет две операции: он выполняет поиск именованного модуля, а затем связывает результаты этого поиска с именем в локальной области видимости. Операция поиска в операторе import определяется как вызов функции __import__() с соответствующими аргументами. Возвращаемое значение __import__() используется для выполнения операции связывания имён в операторе import. Точные сведения об этой операции связывания имён см. в описании оператора import.
Прямой вызов __import__() выполняет только поиск модуля и, если модуль найден, операцию его создания. При этом могут возникать некоторые побочные эффекты, например импорт родительских пакетов и обновление различных кэшей (в том числе sys.modules), но только оператор import выполняет операцию связывания имён.
При выполнении оператора import вызывается стандартная встроенная функция __import__(). Другие механизмы вызова системы импорта (например, importlib.import_module()) могут обходить __import__() и использовать собственные решения для реализации семантики импорта.
При первом импорте модуля Python выполняет его поиск и, если модуль найден, создаёт объект модуля [1] и инициализирует его. Если именованный модуль найти не удаётся, возникает исключение ModuleNotFoundError. При вызове механизма импорта Python применяет различные стратегии поиска именованного модуля. Эти стратегии можно изменять и расширять с помощью различных хуков, описанных в разделах ниже.
Изменено в версии 3.3: Система импорта была обновлена для полной реализации второй фазы PEP 302. Неявного механизма импорта больше нет — вся система импорта доступна через sys.meta_path. Кроме того, реализована встроенная поддержка пакетов пространств имён (см. PEP 420).
5.1. importlib
Модуль importlib предоставляет богатый API для взаимодействия с системой импорта. Например, importlib.import_module() предоставляет рекомендуемый и более простой API для вызова механизма импорта, чем встроенная функция __import__(). Дополнительные сведения см. в документации библиотеки importlib.
5.2. Пакеты
В Python существует только один тип объектов модулей, и все модули относятся к этому типу, независимо от того, реализованы ли они на Python, C или каком-либо другом языке. Для упорядочивания модулей и создания иерархии имён в Python предусмотрено понятие пакетов.
Пакеты можно представить как каталоги в файловой системе, а модули — как файлы в этих каталогах, но не следует воспринимать эту аналогию слишком буквально: пакеты и модули не обязательно должны происходить из файловой системы. В этой документации мы будем использовать эту удобную аналогию с каталогами и файлами. Как и каталоги файловой системы, пакеты организованы иерархически и могут содержать вложенные пакеты, а также обычные модули.
Важно помнить, что все пакеты являются модулями, но не все модули являются пакетами. Иными словами, пакеты — это особый вид модулей. В частности, любой модуль, содержащий атрибут __path__, считается пакетом.
У всех модулей есть имя. Имена вложенных пакетов отделяются от имени родительского пакета точкой, как и в стандартном синтаксисе доступа к атрибутам Python. Так, у вас может быть пакет email, содержащий вложенный пакет email.mime, в котором, в свою очередь, находится модуль email.mime.text.
5.2.1. Обычные пакеты
В Python определены два типа пакетов: обычные пакеты и пакеты пространств имён. Обычные пакеты — это традиционные пакеты, существовавшие в Python 3.2 и более ранних версиях. Обычно обычный пакет реализован в виде каталога, содержащего файл __init__.py. При импорте обычного пакета этот файл __init__.py выполняется неявно, а определённые в нём объекты связываются с именами в пространстве имён пакета. Файл __init__.py может содержать тот же код Python, что и любой другой модуль, а при импорте Python добавит к модулю некоторые дополнительные атрибуты.
Например, следующая структура файловой системы определяет пакет верхнего уровня parent с тремя вложенными пакетами:
parent/
__init__.py
one/
__init__.py
two/
__init__.py
three/
__init__.py
Импорт parent.one неявно выполнит parent/__init__.py и parent/one/__init__.py. При последующих импортах parent.two или parent.three будут выполнены соответственно parent/two/__init__.py и parent/three/__init__.py.
Подкаталог внутри обычного пакета, не содержащий файл __init__.py, считается неявным пакетом пространства имён (вложенным пакетом пространства имён), корнем которого является родительский пакет. См. PEP 420 с описанием соответствующей спецификации.
5.2.2. Пакеты пространств имён
Пакет пространства имён состоит из нескольких частей, каждая из которых добавляет вложенный пакет в родительский пакет. Части могут находиться в разных местах файловой системы. Их также можно обнаружить в ZIP-файлах, в сети или в любом другом месте, где Python выполняет поиск при импорте. Пакеты пространств имён могут как соответствовать объектам файловой системы, так и не соответствовать им; они могут быть виртуальными модулями, не имеющими конкретного представления.
Пакеты пространств имён не используют обычный список в качестве атрибута __path__. Вместо этого они используют пользовательский итерируемый тип, который при следующей попытке импорта внутри такого пакета автоматически выполнит новый поиск частей пакета, если путь родительского пакета (или sys.path для пакета верхнего уровня) изменится.
У пакетов пространств имён нет файла parent/__init__.py. Более того, во время поиска импорта могут быть найдены несколько каталогов parent, каждый из которых предоставлен отдельной частью. Поэтому parent/one может физически находиться не рядом с parent/two. В этом случае Python создаст пакет пространства имён для пакета верхнего уровня parent при его импорте или импорте одного из его вложенных пакетов.
Пакеты пространств имён также могут находиться внутри обычного пакета. Когда система импорта выполняет поиск в атрибуте __path__ обычного пакета и обнаруживает подкаталог, не содержащий файл __init__.py, этот подкаталог становится частью, добавляемой к вложенному пакету пространства имён, находящемуся в содержащем его обычном пакете.
Спецификацию пакетов пространств имён см. также в PEP 420.
5.3. Поиск
Для начала поиска Python требуется полное имя импортируемого модуля (или пакета; для целей этого обсуждения различие несущественно). Это имя может быть получено из различных аргументов оператора import или из параметров функций importlib.import_module() или __import__().
Это имя будет использоваться на различных этапах поиска при импорте и может представлять собой путь с точками к вложенному модулю, например foo.bar.baz. В этом случае Python сначала попытается импортировать foo, затем foo.bar и, наконец, foo.bar.baz. Если какой-либо из промежуточных импортов завершится неудачей, возникнет исключение ModuleNotFoundError.
5.3.1. Кэш модулей
Первое место, проверяемое при поиске во время импорта, — это sys.modules. Это отображение служит кэшем всех ранее импортированных модулей, включая промежуточные пути. Поэтому, если ранее был импортирован foo.bar.baz, в sys.modules будут содержаться записи для foo, foo.bar и foo.bar.baz. Значением каждого ключа будет соответствующий объект модуля.
Во время импорта имя модуля ищется в sys.modules. Если оно там есть, связанное с ним значение является модулем, удовлетворяющим запросу импорта, и процесс завершается. Однако, если значение равно None, возникает исключение ModuleNotFoundError. Если имя модуля отсутствует, Python продолжит поиск модуля.
Содержимое sys.modules можно изменять. Удаление ключа может не уничтожить соответствующий модуль (так как на него могут ссылаться другие модули), но сделает недействительной запись кэша для указанного модуля, заставив Python выполнить новый поиск при следующем импорте этого модуля. Ключу также можно присвоить значение None, чтобы следующий импорт модуля завершился исключением ModuleNotFoundError.
Однако имейте в виду: если вы сохраните ссылку на объект модуля, сделаете недействительной его запись в кэше sys.modules, а затем повторно импортируете именованный модуль, два объекта модуля не будут одним и тем же объектом. Напротив, importlib.reload() повторно использует тот же объект модуля и просто заново инициализирует его содержимое, повторно выполняя код модуля.
5.3.2. Поисковики и загрузчики
Если именованный модуль не найден в sys.modules, Python вызывает протокол импорта для поиска и загрузки модуля. Этот протокол состоит из двух концептуальных объектов: поисковиков и загрузчиков. Задача поисковика — определить, может ли он найти именованный модуль, используя известную ему стратегию. Объекты, реализующие оба этих интерфейса, называются импортёрами: если они могут загрузить запрошенный модуль, они возвращают сами себя.
Python включает несколько стандартных поисковиков и импортёров. Первый умеет находить встроенные модули, а второй — замороженные модули. Третий стандартный поисковик ищет модули по пути импорта. Путь импорта — это список местоположений, в котором могут указываться пути файловой системы или ZIP-файлы. Его также можно расширить для поиска любых доступных ресурсов, например ресурсов, указанных URL-адресами.
Механизм импорта можно расширять, добавляя новые поисковики, чтобы расширить диапазон и область поиска модулей.
Поисковики сами не загружают модули. Если они могут найти именованный модуль, то возвращают спецификацию модуля — объект, содержащий информацию об импорте модуля, которую механизм импорта затем использует при загрузке модуля.
В следующих разделах подробнее описан протокол для поисковиков и загрузчиков, а также способы создания и регистрации новых объектов для расширения механизма импорта.
Изменено в версии 3.4: В предыдущих версиях Python поисковики напрямую возвращали загрузчики, тогда как теперь они возвращают спецификации модулей, которые содержат загрузчики. Загрузчики по-прежнему используются при импорте, но их обязанности сократились.
5.3.3. Хуки импорта
Механизм импорта разработан с возможностью расширения; основной механизм для этого — хуки импорта. Существует два типа хуков импорта: метахуки и хуки пути импорта.
Метахуки вызываются в начале обработки импорта, до выполнения любой другой обработки импорта, за исключением проверки кэша sys.modules. Это позволяет метахукам переопределять обработку sys.path, замороженных модулей и даже встроенных модулей. Метахуки регистрируются добавлением новых объектов-поисковиков в sys.meta_path, как описано ниже.
Хуки пути импорта вызываются в ходе обработки sys.path (или package.__path__), когда встречается связанный с ними элемент пути. Хуки пути импорта регистрируются добавлением новых вызываемых объектов в sys.path_hooks, как описано ниже.
5.3.4. Метапуть
Если именованный модуль не найден в sys.modules, Python затем выполняет поиск в sys.meta_path, содержащем список объектов-поисковиков метапути. Эти поисковики опрашиваются по очереди, чтобы определить, знают ли они, как обработать именованный модуль. Поисковики метапути должны реализовывать метод find_spec(), принимающий три аргумента: имя, путь импорта и (необязательно) целевой модуль. Поисковик метапути может использовать любую стратегию, чтобы определить, может ли он обработать именованный модуль.
Если поисковик метапути знает, как обработать именованный модуль, он возвращает объект спецификации. Если он не может обработать именованный модуль, то возвращает None. Если при обработке sys.meta_path список поисковиков заканчивается и ни один из них не вернул спецификацию, возникает исключение ModuleNotFoundError. Любые другие возникшие исключения просто передаются выше по стеку и прерывают процесс импорта.
Метод find_spec() поисковиков метапути вызывается с двумя или тремя аргументами. Первый аргумент — полное имя импортируемого модуля, например foo.bar.baz. Второй аргумент — записи пути, используемые для поиска модуля. Для модулей верхнего уровня второй аргумент равен None, а для вложенных модулей или пакетов — значению атрибута __path__ родительского пакета. Если получить доступ к соответствующему атрибуту __path__ не удаётся, возникает исключение ModuleNotFoundError. Третий аргумент — существующий объект модуля, который позднее станет целевым объектом загрузки. Система импорта передаёт целевой модуль только при перезагрузке.
Для одного запроса на импорт метапуть может обходиться несколько раз. Например, если ни один из задействованных модулей ещё не кэширован, импорт foo.bar.baz сначала выполнит импорт верхнего уровня, вызывая mpf.find_spec("foo", None, None) у каждого поисковика метапути (mpf). После импорта foo модуль foo.bar будет импортирован при повторном обходе метапути с вызовом mpf.find_spec("foo.bar", foo.__path__, None). После импорта foo.bar при последнем обходе будет вызван mpf.find_spec("foo.bar.baz", foo.bar.__path__, None).
Некоторые поисковики метапути поддерживают только импорт модулей верхнего уровня. Такие импортёры всегда возвращают None, если в качестве второго аргумента передано что-либо, кроме None.
В стандартном списке sys.meta_path Python содержатся три поисковика метапути: один умеет импортировать встроенные модули, второй — замороженные модули, а третий — модули из пути импорта (то есть поисковик на основе пути).
Изменено в версии 3.4: Метод find_spec() поисковиков метапути заменил find_module(), который теперь считается устаревшим. Он продолжит работать без изменений, но механизм импорта будет использовать его, только если поисковик не реализует find_spec().
Изменено в версии 3.10: Использование find_module() системой импорта теперь вызывает предупреждение ImportWarning.
Изменено в версии 3.12: find_module() удалён. Вместо него используйте find_spec().
5.4. Загрузка
Если и когда будет найдена спецификация модуля, механизм импорта будет использовать её (и содержащийся в ней загрузчик) при загрузке модуля. Ниже приведено приблизительное описание того, что происходит во время загрузки при импорте:
module = None
if spec.loader is not None and hasattr(spec.loader, 'create_module'):
# It is assumed 'exec_module' will also be defined on the loader.
module = spec.loader.create_module(spec)
if module is None:
module = ModuleType(spec.name)
# The import-related module attributes get set here:
_init_module_attrs(spec, module)
if spec.loader is None:
# unsupported
raise ImportError
if spec.origin is None and spec.submodule_search_locations is not None:
# namespace package
sys.modules[spec.name] = module
elif not hasattr(spec.loader, 'exec_module'):
module = spec.loader.load_module(spec.name)
else:
sys.modules[spec.name] = module
try:
spec.loader.exec_module(module)
except BaseException:
try:
del sys.modules[spec.name]
except KeyError:
pass
raise
return sys.modules[spec.name]
Обратите внимание на следующие детали:
- Если в
sys.modulesуже есть объект модуля с указанным именем, импорт уже вернул бы его. - Модуль будет находиться в
sys.modulesдо выполнения загрузчиком кода модуля. Это крайне важно, поскольку код модуля может (прямо или косвенно) импортировать сам себя; предварительное добавление его вsys.modulesпредотвращает бесконечную рекурсию в худшем случае и повторную загрузку в лучшем. - Если загрузка завершается с ошибкой, из
sys.modulesудаляется сбойный модуль — и только он. Любой модуль, уже находящийся в кэшеsys.modules, и любой модуль, успешно загруженный в качестве побочного эффекта, должны остаться в кэше. Это отличается от повторной загрузки, при которой даже сбойный модуль остаётся вsys.modules. - После создания модуля, но до его выполнения, механизм импорта задаёт атрибуты модуля, связанные с импортом («_init_module_attrs» в приведённом выше псевдокоде), как описано в следующем разделе.
- Выполнение модуля — ключевой этап загрузки, на котором заполняется пространство имён модуля. Выполнение полностью делегируется загрузчику, который определяет, что и каким образом будет заполнено.
- Модуль, созданный во время загрузки и переданный в exec_module(), может отличаться от модуля, возвращаемого в конце импорта [2].
Изменено в версии 3.4: Система импорта взяла на себя типовые обязанности загрузчиков. Ранее они выполнялись методом importlib.abc.Loader.load_module().
5.4.1. Загрузчики
Загрузчики модулей выполняют ключевую функцию загрузки — выполнение модуля. Механизм импорта вызывает метод importlib.abc.Loader.exec_module() с одним аргументом — объектом модуля, который нужно выполнить. Любое значение, возвращённое из exec_module(), игнорируется.
Загрузчики должны соответствовать следующим требованиям:
- Если модуль является модулем Python (а не встроенным модулем или динамически загружаемым расширением), загрузчик должен выполнить код модуля в глобальном пространстве имён этого модуля (
module.__dict__). - Если загрузчик не может выполнить модуль, он должен вызвать исключение
ImportError, хотя любое другое исключение, возникшее во время выполненияexec_module(), будет передано дальше.
Во многих случаях поисковик и загрузчик могут быть одним и тем же объектом; в таких случаях метод find_spec() просто вернёт спецификацию, в которой загрузчиком задано self.
Загрузчики модулей могут взять на себя создание объекта модуля во время загрузки, реализовав метод create_module(). Он принимает один аргумент — спецификацию модуля — и возвращает новый объект модуля, используемый во время загрузки. create_module() не нужно задавать атрибуты объекта модуля. Если метод возвращает None, механизм импорта создаст новый модуль самостоятельно.
Добавлено в версии 3.4: Метод загрузчиков create_module().
Изменено в версии 3.4: Метод load_module() был заменён на exec_module(), а механизм импорта взял на себя все типовые обязанности по загрузке.
Для совместимости с существующими загрузчиками механизм импорта будет использовать метод load_module() загрузчиков, если он существует, а загрузчик при этом не реализует exec_module(). Однако load_module() устарел, и загрузчикам следует вместо него реализовать exec_module().
Метод load_module() должен выполнять все описанные выше типовые операции загрузки, а также выполнять модуль. Действуют те же ограничения, а также следующие уточнения:
- Если в
sys.modulesуже есть объект модуля с указанным именем, загрузчик должен использовать этот существующий модуль. (В противном случаеimportlib.reload()не будет работать правильно.) Если именованного модуля нет вsys.modules, загрузчик должен создать новый объект модуля и добавить его вsys.modules. - Модуль должен находиться в
sys.modulesдо того, как загрузчик выполнит код модуля, чтобы предотвратить бесконечную рекурсию или повторную загрузку. - Если загрузка завершается с ошибкой, загрузчик должен удалить все добавленные им модули из
sys.modules, но удалить он должен только сбойные модули и только в том случае, если загрузчик явно загрузил их сам.
Изменено в версии 3.5: Если определён exec_module(), но не create_module(), вызывается предупреждение DeprecationWarning.
Изменено в версии 3.6: Если определён exec_module(), но не create_module(), вызывается исключение ImportError.
Изменено в версии 3.10: Использование load_module() вызовет предупреждение ImportWarning.
5.4.2. Подмодули
Когда подмодуль загружается любым способом (например, с помощью API importlib, инструкций import или import-from либо встроенного __import__()), в пространстве имён родительского модуля создаётся привязка к объекту подмодуля. Например, если у пакета spam есть подмодуль foo, то после импорта spam.foo у spam появится атрибут foo, связанный с подмодулем. Допустим, у вас имеется следующая структура каталогов:
spam/
__init__.py
foo.py
и в spam/__init__.py есть следующая строка:
from .foo import Foo
тогда выполнение следующего кода создаёт привязки имён для foo и Foo в модуле spam:
>>> import spam >>> spam.foo <module 'spam.foo' from '/tmp/imports/spam/foo.py'> >>> spam.Foo <class 'spam.foo.Foo'>
С учётом знакомых правил привязки имён в Python это может показаться неожиданным, но на самом деле это фундаментальная особенность системы импорта. Соблюдается инвариант: если у вас есть sys.modules['spam'] и sys.modules['spam.foo'] (как после приведённого выше импорта), то последнее должно быть атрибутом foo первого.
5.4.3. Спецификации модулей
Во время импорта механизм импорта использует различные сведения о каждом модуле, особенно до его загрузки. Большая часть этих сведений одинакова для всех модулей. Назначение спецификации модуля — инкапсулировать эту связанную с импортом информацию отдельно для каждого модуля.
Использование спецификации при импорте позволяет передавать состояние между компонентами системы импорта, например между поисковиком, создающим спецификацию модуля, и загрузчиком, выполняющим его. Что особенно важно, это позволяет механизму импорта выполнять типовые операции загрузки, тогда как без спецификации модуля за них отвечал загрузчик.
Спецификация модуля доступна как module.__spec__. Соответствующая настройка __spec__ применяется также к модулям, инициализированным во время запуска интерпретатора. Единственное исключение — __main__, где __spec__ в некоторых случаях равен None.
Подробнее о содержимом спецификации модуля см. в разделе ModuleSpec.
Добавлено в версии 3.4.
5.4.4. Атрибуты __path__ модулей
Атрибут __path__ должен быть (возможно, пустой) последовательностью строк, перечисляющих расположения, в которых будут находиться подмодули пакета. По определению, если у модуля есть атрибут __path__, он является пакетом.
Атрибут __path__ пакета используется при импорте его подпакетов. В механизме импорта он выполняет примерно ту же роль, что и sys.path, то есть предоставляет список расположений для поиска модулей во время импорта. Однако __path__ обычно имеет гораздо более ограниченную область действия, чем sys.path.
К __path__ пакета применяются те же правила, что и к sys.path. При обходе __path__ пакета используются sys.path_hooks (описанные ниже).
Файл __init__.py пакета может задавать или изменять атрибут __path__ пакета; обычно именно так реализовывались пакеты пространств имён до PEP 420. После принятия PEP 420 пакетам пространств имён больше не нужно предоставлять файлы __init__.py, содержащие только код для изменения __path__; механизм импорта автоматически правильно задаёт __path__ для пакета пространства имён.
5.4.5. Строковые представления модулей
По умолчанию у всех модулей есть пригодное для использования строковое представление, однако в зависимости от заданных выше атрибутов и спецификации модуля можно более явно управлять строковым представлением объектов модулей.
Если у модуля есть спецификация (__spec__), механизм импорта попытается сформировать на её основе строковое представление. Если это не удастся или спецификации нет, система импорта сформирует представление по умолчанию, используя доступные сведения о модуле. В качестве входных данных для представления будут использоваться module.__name__, module.__file__ и module.__loader__; для отсутствующих сведений будут использоваться значения по умолчанию.
Применяются следующие точные правила:
- Если у модуля есть атрибут
__spec__, для формирования строкового представления используются сведения из спецификации. Проверяются атрибуты «name», «loader», «origin» и «has_location». - Если у модуля есть атрибут
__file__, он используется как часть строкового представления модуля. - Если у модуля нет
__file__, но есть__loader__, не равныйNone, строковое представление загрузчика используется как часть строкового представления модуля. - В противном случае в строковом представлении используется только
__name__модуля.
Изменено в версии 3.12: Использование module_repr(), устаревшее с Python 3.4, было удалено в Python 3.12; этот метод больше не вызывается при формировании строкового представления модуля.
5.4.6. Инвалидация кэшированного байт-кода
Перед загрузкой кэшированного байт-кода из файла .pyc Python проверяет, соответствует ли кэш актуальному исходному файлу .py. По умолчанию при записи кэша Python сохраняет в нём временную метку последнего изменения исходного файла и его размер. Во время выполнения система импорта проверяет файл кэша, сравнивая сохранённые в нём метаданные с метаданными исходного файла.
Python также поддерживает файлы кэша на основе хеша, в которых вместо метаданных хранится хеш содержимого исходного файла. Существует два варианта файлов .pyc на основе хеша: проверяемые и непроверяемые. Для проверяемых файлов .pyc на основе хеша Python проверяет файл кэша, вычисляя хеш исходного файла и сравнивая полученный результат с хешем в файле кэша. Если проверяемый файл кэша на основе хеша признан недействительным, Python создаёт его заново и записывает новый проверяемый файл кэша на основе хеша. Для непроверяемых файлов .pyc на основе хеша Python просто считает файл кэша действительным, если он существует. Поведение проверки файлов .pyc на основе хеша можно переопределить с помощью флага --check-hash-based-pycs.
Изменено в версии 3.7: Добавлены файлы .pyc на основе хеша. Ранее Python поддерживал только инвалидацию кэша байт-кода на основе временных меток.
5.5. Поиск на основе путей
Как упоминалось ранее, Python включает несколько стандартных метапоисковиков путей. Один из них, называемый поисковиком на основе путей (PathFinder), выполняет поиск по пути импорта, содержащему список элементов пути. Каждый элемент пути задаёт расположение, в котором следует искать модули.
Сам поисковик на основе путей не умеет импортировать что-либо. Вместо этого он обходит отдельные элементы пути, сопоставляя каждый из них с поисковиком элементов пути, который умеет обрабатывать соответствующий тип пути.
Стандартный набор поисковиков элементов пути реализует всю семантику поиска модулей в файловой системе, обрабатывая специальные типы файлов, такие как исходный код Python (файлы .py), байт-код Python (файлы .pyc) и разделяемые библиотеки (например, файлы .so). Если это поддерживается модулем zipimport из стандартной библиотеки, стандартные поисковики элементов пути также умеют загружать из ZIP-архивов все эти типы файлов, кроме разделяемых библиотек.
Элементы пути не обязательно должны указывать только на расположения в файловой системе. Они могут указывать на URL-адреса, запросы к базам данных или любые другие расположения, которые можно задать в виде строки.
Поисковик на основе путей предоставляет дополнительные точки расширения и протоколы, позволяющие расширять и настраивать типы путей, по которым можно выполнять поиск. Например, если вы хотите поддержать элементы пути в виде сетевых URL-адресов, можно написать обработчик, реализующий семантику HTTP для поиска модулей в интернете. Этот обработчик (вызываемый объект) вернёт поисковик элементов пути, поддерживающий описанный ниже протокол; затем с его помощью можно будет получить загрузчик модуля из интернета.
Предупреждение: в этом разделе и предыдущем используется термин поисковик, а различие между ними обозначается терминами метапоисковик путей и поисковик элементов пути. Эти два типа поисковиков очень похожи, поддерживают схожие протоколы и работают схожим образом в процессе импорта, однако важно помнить об их тонких различиях. В частности, метапоисковики путей работают в начале процесса импорта, при обходе sys.meta_path.
В отличие от них, поисковики элементов пути в некотором смысле являются деталью реализации поисковика на основе путей. Более того, если удалить поисковик на основе путей из sys.meta_path, семантика поисковиков элементов пути вообще не будет задействована.
5.5.1. Поисковики элементов пути
Поисковик на основе путей отвечает за поиск и загрузку модулей и пакетов Python, расположение которых задаётся строковым элементом пути. Большинство элементов пути указывают на расположения в файловой системе, но это не обязательно.
Будучи метапоисковиком путей, поисковик на основе путей реализует ранее описанный протокол find_spec(), однако он предоставляет дополнительные точки расширения, которые можно использовать для настройки поиска и загрузки модулей по пути импорта.
Поисковик на основе путей использует три переменные: sys.path, sys.path_hooks и sys.path_importer_cache. Также используются атрибуты __path__ объектов пакетов. Они предоставляют дополнительные способы настройки механизма импорта.
sys.path содержит список строк, задающих расположения для поиска модулей и пакетов. Этот список инициализируется из переменной окружения PYTHONPATH и различных других значений по умолчанию, специфичных для установки и реализации. Элементы sys.path могут задавать каталоги в файловой системе, ZIP-файлы и потенциально другие «расположения» (см. модуль site), в которых следует искать модули, например URL-адреса или запросы к базам данных. В sys.path должны находиться только строки; все остальные типы данных игнорируются.
Поисковик на основе путей — это метапоисковик путей, поэтому механизм импорта начинает поиск по пути импорта, вызывая метод find_spec() поисковика на основе путей, как описано ранее. Если указан аргумент path для find_spec(), он будет представлять собой список строковых путей для обхода — обычно это атрибут __path__ пакета при импорте внутри этого пакета. Если аргумент path имеет значение None, это означает импорт верхнего уровня, и используется sys.path.
Поисковик на основе путей перебирает все элементы пути поиска и для каждого из них ищет подходящий поисковик элементов пути (PathEntryFinder). Поскольку эта операция может быть затратной (например, поиск может повлечь накладные расходы на вызов stat()), поисковик на основе путей поддерживает кэш, сопоставляющий элементы пути с поисковиками элементов пути. Этот кэш хранится в sys.path_importer_cache (несмотря на название, в этом кэше хранятся объекты поисковиков, а не только объекты импортёров). Таким образом, затратный поиск поисковика элементов пути для конкретного расположения, заданного элементом пути, достаточно выполнить только один раз. Пользовательский код может удалять записи кэша из sys.path_importer_cache, заставляя поисковик на основе путей повторно выполнять поиск по элементу пути.
Если элемента пути нет в кэше, поисковик на основе путей перебирает все вызываемые объекты в sys.path_hooks. Каждый из обработчиков элементов пути в этом списке вызывается с одним аргументом — элементом пути, который нужно проверить. Этот вызываемый объект может вернуть поисковик элементов пути, способный обработать элемент пути, либо вызвать исключение ImportError. Поисковик на основе путей использует ImportError, чтобы сообщить, что обработчик не может найти поисковик элементов пути для этого элемента пути. Исключение игнорируется, и обход пути импорта продолжается. Обработчик должен ожидать строку или объект bytes; кодировку объектов bytes обработчик выбирает самостоятельно (например, это может быть системная кодировка файловой системы, UTF-8 или какая-либо другая). Если обработчик не может декодировать аргумент, он должен вызвать ImportError.
Если перебор sys.path_hooks завершается без возврата поисковика элементов пути, метод find_spec() поисковика на основе путей сохраняет None в sys.path_importer_cache (чтобы указать, что для этого элемента пути поисковик отсутствует) и возвращает None, сообщая, что этот метапоисковик путей не смог найти модуль.
Если один из вызываемых объектов обработчика элементов пути в sys.path_hooks возвращает поисковик элементов пути, то для запроса спецификации модуля у поисковика используется следующий протокол. Эта спецификация затем применяется при загрузке модуля.
Текущий рабочий каталог, обозначаемый пустой строкой, обрабатывается несколько иначе, чем другие элементы в sys.path. Во-первых, если текущий рабочий каталог невозможно определить или он не существует, никакое значение не сохраняется в sys.path_importer_cache. Во-вторых, значение для текущего рабочего каталога заново определяется при каждом поиске модуля. В-третьих, путь, используемый для sys.path_importer_cache и возвращаемый методом importlib.machinery.PathFinder.find_spec(), будет фактическим текущим рабочим каталогом, а не пустой строкой.
5.5.2. Протокол поисковика элементов пути
Чтобы поддерживать импорт модулей и инициализированных пакетов, а также добавлять части в пакеты пространства имён, поисковики элементов пути должны реализовывать метод find_spec().
find_spec() принимает два аргумента: полное имя импортируемого модуля и целевой модуль (необязательный). find_spec() возвращает полностью заполненную спецификацию модуля. У этой спецификации всегда будет задано значение «loader» (за одним исключением).
Чтобы сообщить механизму импорта, что спецификация представляет часть пространства имён, поисковик элементов пути устанавливает для submodule_search_locations список, содержащий эту часть.
Изменено в версии 3.4: find_spec() заменил find_loader() и find_module(). Оба метода теперь устарели, но будут использоваться, если find_spec() не определён.
Старые поисковики элементов пути могут реализовывать один из этих двух устаревших методов вместо find_spec(). Для обратной совместимости эти методы по-прежнему поддерживаются. Однако если поисковик элементов пути реализует find_spec(), устаревшие методы игнорируются.
find_loader() принимает один аргумент — полное имя импортируемого модуля. find_loader() возвращает 2-кортеж, в котором первый элемент — загрузчик, а второй — часть пространства имён.
Для обратной совместимости с другими реализациями протокола импорта многие поисковики элементов пути также поддерживают тот же традиционный метод find_module(), что и метапоисковики путей. Однако методы find_module() поисковиков элементов пути никогда не вызываются с аргументом path (предполагается, что соответствующая информация о пути сохраняется при первоначальном вызове обработчика пути).
Метод find_module() поисковиков элементов пути устарел, поскольку не позволяет поисковику элементов пути добавлять части в пакеты пространства имён. Если поисковик элементов пути реализует оба метода — find_loader() и find_module(), — система импорта всегда будет предпочитать find_loader() методу find_module().
Изменено в версии 3.10: Вызовы find_module() и find_loader() системой импорта вызывают исключение ImportWarning.
Изменено в версии 3.12: Методы find_module() и find_loader() удалены.
5.6. Замена стандартной системы импорта
Самый надёжный способ заменить всю систему импорта — удалить стандартное содержимое sys.meta_path и полностью заменить его пользовательским метапоисковым обработчиком.
Если достаточно изменить только поведение операторов импорта, не затрагивая другие API, обращающиеся к системе импорта, может быть достаточно заменить встроенную функцию __import__().
Чтобы выборочно запретить импорт некоторых модулей с помощью обработчика в начале метапути (вместо полного отключения стандартной системы импорта), достаточно непосредственно вызвать ModuleNotFoundError из find_spec(), а не возвращать None. Последнее означает, что поиск по метапути должен продолжиться, тогда как вызов исключения немедленно его прерывает.
5.7. Относительный импорт пакетов
В относительном импорте используются точки в начале имени. Одна точка в начале обозначает относительный импорт, начинающийся с текущего пакета. Две или более точки в начале обозначают относительный импорт из родительского пакета или пакетов текущего пакета: каждая точка после первой соответствует одному уровню вверх. Например, для следующей структуры пакетов:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
Как в subpackage1/moduleX.py, так и в subpackage1/__init__.py допустимы следующие относительные импорты:
from .moduleY import spam from .moduleY import spam as ham from . import moduleY from ..subpackage1 import moduleY from ..subpackage2.moduleZ import eggs from ..moduleA import foo
В абсолютном импорте можно использовать как синтаксис import <>, так и from <> import <>, но в относительном импорте допустима только вторая форма. Причина в том, что:
import XXX.YYY.ZZZ
должен предоставлять XXX.YYY.ZZZ в качестве допустимого выражения, а .moduleY не является допустимым выражением.
5.8. Особенности __main__
Модуль __main__ занимает особое место в системе импорта Python. Как отмечалось в другом месте, модуль __main__ инициализируется непосредственно при запуске интерпретатора, подобно sys и builtins. Однако, в отличие от этих двух модулей, он формально не считается встроенным модулем. Это связано с тем, что способ инициализации __main__ зависит от флагов и других параметров, с которыми запускается интерпретатор.
5.8.1. __main__.__spec__
В зависимости от способа инициализации __main__ атрибут __main__.__spec__ получает соответствующее значение либо значение None.
При запуске Python с параметром -m значению __spec__ присваивается спецификация соответствующего модуля или пакета. __spec__ также заполняется, когда модуль __main__ загружается в процессе выполнения каталога, ZIP-файла или другого элемента sys.path.
Во всех остальных случаях __main__.__spec__ получает значение None, поскольку код, используемый для заполнения __main__, не соответствует напрямую импортируемому модулю:
- интерактивная оболочка
-
параметр
-c - запуск из стандартного ввода
- непосредственный запуск из исходного файла или файла байт-кода
Обратите внимание, что в последнем случае __main__.__spec__ всегда имеет значение None, даже если файл технически можно напрямую импортировать как модуль. Используйте параметр -m, если в __main__ должны присутствовать корректные метаданные модуля.
Также обратите внимание, что даже если __main__ соответствует импортируемому модулю и __main__.__spec__ установлен соответствующим образом, они всё равно считаются разными модулями. Это связано с тем, что блоки, защищённые проверками if __name__ == "__main__":, выполняются только тогда, когда модуль используется для заполнения пространства имён __main__, но не при обычном импорте.
5.9. Ссылки
Механизм импорта значительно изменился со времён ранних версий Python. Первоначальную спецификацию пакетов по-прежнему можно прочитать, хотя с момента её написания некоторые детали изменились.
Первоначальной спецификацией для sys.meta_path был документ PEP 302, впоследствии расширенный документом PEP 420.
PEP 420 ввёл пакеты пространства имён в Python 3.3. Документ PEP 420 также ввёл протокол find_loader() как альтернативу find_module().
PEP 366 описывает добавление атрибута __package__ для явного относительного импорта в главных модулях.
PEP 328 ввёл абсолютный и явный относительный импорт, а также первоначально предложил __name__ для семантики, которую PEP 366 впоследствии определил для __package__.
PEP 338 определяет выполнение модулей в качестве скриптов.
PEP 451 добавляет инкапсуляцию состояния импорта каждого модуля в объектах спецификаций. Он также переносит большую часть стандартных обязанностей загрузчиков обратно на механизм импорта. Эти изменения позволяют объявить устаревшими несколько API системы импорта, а также добавить новые методы для поисковиков и загрузчиков.
Сноски
© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/reference/import.html