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

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

Код на Python в одном модуле получает доступ к коду в другом модуле через процесс импортирования. Оператор import – самый распространённый способ вызова механизма импорта, но не единственный. Такие функции, как importlib.import_module() и встроенная __import__(), также могут использоваться для вызова механизма импорта.

Оператор import объединяет две операции: он ищет указанный модуль, а затем привязывает результаты поиска к имени в локальной области видимости. Операция поиска оператора import определяется как вызов функции __import__() с соответствующими аргументами. Возвращаемое значение __import__() используется для выполнения операции привязки имени оператора import. См. оператор import для точных подробностей этой операции привязки имени.

Прямой вызов __import__() выполняет только поиск модуля и, если найден, операцию создания модуля. Хотя могут возникать некоторые побочные эффекты, такие как импорт родительских пакетов и обновление различных кешей (включая sys.modules), только оператор import выполняет операцию привязки имени.

При вызове __import__() как части оператора import система импорта сначала проверяет глобальное пространство имён модуля на наличие функции с таким именем. Если она не найдена, вызывается стандартная встроенная __import__(). Другие механизмы вызова системы импорта (например, importlib.import_module()) не выполняют эту проверку и всегда используют стандартную систему импорта.

Когда модуль импортируется впервые, Python ищет модуль и, если найден, создаёт объект модуля [1], инициализируя его. Если указанный модуль не найден, возбуждается ImportError. Python реализует различные стратегии поиска указанного модуля при вызове механизма импорта. Эти стратегии можно изменять и расширять с помощью различных хуков, описанных в следующих разделах.

Изменено в версии 3.3: Система импорта была обновлена для полной реализации второй фазы PEP 302. Больше нет неявного механизма импорта – полная система импорта раскрывается через sys.meta_path. Кроме того, реализована поддержка нативных пакетов пространств имён (см. PEP 420).

5.1. importlib

Модуль importlib предоставляет богатый API для взаимодействия с системой импорта. Например, importlib.import_module() предоставляет рекомендуемый, более простой API, чем встроенная __import__(), для вызова механизма импорта. Обратитесь к библиотечной документации importlib за дополнительными подробностями.

5.2. ПакетыPackages

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

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

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

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

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

Python определяет два типа пакетов: обычные пакеты и пакеты пространств имён. Обычные пакеты – это традиционные пакеты, существовавшие в 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. Пакеты пространства имёнNamespace packages

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

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

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

See also PEP 420 for the namespace package specification.

5.3. ПоискSearching

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

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

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

Первое место, проверяемое во время поиска импорта – это sys.modules. Это отображение служит кешем всех ранее импортированных модулей, включая промежуточные пути. Так, если foo.bar.baz был ранее импортирован, sys.modules будет содержать записи для foo, foo.bar и foo.bar.baz. Каждый ключ будет иметь в качестве значения соответствующий объект модуля.

Во время импорта имя модуля ищется в sys.modules, и если оно присутствует, соответствующее значение является модулем, удовлетворяющим импорту, и процесс завершается. Однако если значение равно None, то возбуждается ImportError. Если имя модуля отсутствует, Python продолжит поиск модуля.

sys.modules доступен для записи. Удаление ключа может не уничтожить связанный модуль (поскольку другие модули могут содержать ссылки на него), но оно сделает запись в кеше для указанного модуля недействительной, что вынудит Python выполнить новый поиск этого модуля при следующем импорте. Ключу также можно присвоить значение None, что приведёт к тому, что следующий импорт модуля закончится ImportError.

Однако следует помнить: если вы сохраните ссылку на объект модуля, сделаете недействительной его запись в кеше в sys.modules и затем повторно импортируете указанный модуль, два объекта модуля не будут одинаковыми. Напротив, importlib.reload() повторно использует тот же объект модуля и просто переинициализирует содержимое модуля, заново выполняя его код.

5.3.2. Искатели и загрузчикиFinders and loaders

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

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

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

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

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

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

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

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

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

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

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

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

Если мета-пути находитель знает, как обработать указанный модуль, он возвращает объект спецификации. Если он не может обработать указанный модуль, он возвращает None. Если обработка sys.meta_path достигает конца своего списка, не вернув спецификацию, возбуждается ImportError. Любые другие возбуждённые исключения просто распространяются вверх, прерывая процесс импорта.

Метод find_spec() мета-пути находителей вызывается с двумя или тремя аргументами. Первый – полностью квалифицированное имя импортируемого модуля, например foo.bar.baz. Второй аргумент – это записи пути для использования при поиске модуля. Для модулей верхнего уровня второй аргумент равен None, но для подмодулей или подпакетов второй аргумент – это значение атрибута __path__ родительского пакета. Если соответствующий атрибут __path__ не может быть получен, возбуждается ImportError. Третий аргумент – существующий объект модуля, который позже станет целью загрузки. Система импорта передаёт целевой модуль только во время перезагрузки.

Мета-путь может быть пройден несколько раз для одного запроса импорта. Например, если предположить, что ни один из задействованных модулей ещё не был кеширован, импорт foo.bar.baz сначала выполнит импорт верхнего уровня, вызывая mpf.find_spec("foo", None, None) для каждого мета-пути находителя (mpf). После того, как foo импортирован, foo.bar будет импортирован при повторном прохождении мета-пути с вызовом mpf.find_spec("foo.bar", foo.__path__, None). Как только foo.bar импортирован, финальный проход вызовет mpf.find_spec("foo.bar.baz", foo.bar.__path__, None).

Некоторые мета-пути находители поддерживают только импорт верхнего уровня. Эти импортёры всегда возвращают None, если в качестве второго аргумента передаётся что-либо, кроме None.

Стандартный sys.meta_path Python содержит три мета-пути находителя: один, который знает, как импортировать встроенные модули, один, который знает, как импортировать замороженные модули, и один, который знает, как импортировать модули из пути импорта (т.е. находитель на основе пути).

Изменено в версии 3.4: Метод find_spec() мета-пути находителей заменил find_module(), который теперь является устаревшим. Хотя он продолжит работать без изменений, механизм импорта будет пытаться использовать его только в том случае, если находитель не реализует find_spec().

5.4. ЗагрузкаLoading

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

module = None
if spec.loader is not None and hasattr(spec.loader, 'create_module'):
    module = spec.loader.create_module(spec)
if module is None:
    module = ModuleType(spec.name)
# Здесь задаются атрибуты модуля, связанные с импортом:
_init_module_attrs(spec, module)

if spec.loader is None:
    if spec.submodule_search_locations is not None:
        # пакет пространства имён
        sys.modules[spec.name] = module
    else:
        # не поддерживается
        raise ImportError
elif not hasattr(spec.loader, 'exec_module'):
    module = spec.loader.load_module(spec.name)
    # Устанавливает __loader__ и __package__, если они отсутствуют.
else:
    sys.modules[spec.name] = module
    try:
        spec.loader.exec_module(module)
    except BaseException:
        try:
            del sys.modules[spec.name]
        except KeyError:
            pass
        raise
return sys.modules[spec.name]

Обратите внимание на следующие детали:

  • Если объект модуля с указанным именем уже существует в sys.modules, импорт уже вернул его.
  • Модуль будет существовать в sys.modules до того, как загрузчик выполнит код модуля. Это крайне важно, поскольку код модуля может (прямо или косвенно) импортировать сам себя; добавление его в sys.modules заранее предотвращает неограниченную рекурсию в худшем случае и многократную загрузку – в лучшем.
  • Если загрузка не удалась, сбойный модуль – и только он – удаляется из sys.modules. Любой модуль, уже находящийся в sys.modules кеше, а также любой модуль, успешно загруженный как побочный эффект, должен оставаться в кеше. Это отличается от перезагрузки, где даже сбойный модуль остаётся в sys.modules.
  • После создания модуля, но до его выполнения, механизм импорта устанавливает атрибуты модуля, связанные с импортом («_init_module_attrs» в примере псевдокода выше), как описано в следующем разделе.
  • Выполнение модуля – ключевой момент загрузки, в ходе которого заполняется пространство имён модуля. Выполнение полностью делегируется загрузчику, который решает, что и как заполнять.
  • Модуль, созданный во время загрузки и переданный в exec_module(), может не совпадать с тем, что возвращается в конце импорта [2].

Изменено в версии 3.4: Система импорта взяла на себя шаблонные обязанности загрузчиков. Ранее они выполнялись методом importlib.abc.Loader.load_module().

5.4.1. ЗагрузчикиLoaders

Загрузчики модулей обеспечивают критическую функцию загрузки: выполнение модуля. Механизм импорта вызывает метод importlib.abc.Loader.exec_module() с одним аргументом – объектом модуля для выполнения. Любое возвращаемое значение exec_module() игнорируется.

Загрузчики должны удовлетворять следующим требованиям:

  • Если модуль является Python-модулем (в отличие от встроенного модуля или динамически загружаемого расширения), загрузчик должен выполнить код модуля в глобальном пространстве имён модуля (module.__dict__).
  • Если загрузчик не может выполнить модуль, он должен возбудить исключение ImportError, хотя любое другое исключение, возникшее во время exec_module(), будет распространено.

Во многих случаях искатель и загрузчик могут быть одним и тем же объектом; в таких случаях метод find_spec() просто вернёт спецификацию, в которой загрузчиком установлено self.

Загрузчики модулей могут принять участие в создании объекта модуля во время загрузки, реализовав метод create_module(). Он принимает один аргумент – спецификацию модуля – и возвращает новый объект модуля для использования при загрузке. create_module() не обязан устанавливать какие-либо атрибуты на объекте модуля. Если загрузчик не определяет create_module(), механизм импорта создаст новый модуль самостоятельно.

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

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

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

spam/
    __init__.py
    foo.py
    bar.py

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

from .foo import Foo
from .bar import Bar

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

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

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

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

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

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

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

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

5.4.5. module.__path__

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Three variables are used by the path based finder, sys.path, sys.path_hooks and sys.path_importer_cache. The __path__ attributes on package objects are also used. These provide additional ways that the import machinery can be customized.

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 (несмотря на название, этот кэш на самом деле хранит объекты искателей, а не ограничивается объектами importer). Таким образом, дорогостоящий поиск искателя записей пути для конкретного местоположения записи пути выполняется только один раз. Пользовательский код может свободно удалять записи кэша из 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, то используется следующий протокол для запроса спецификации модуля у искателя, которая затем используется при загрузке модуля.

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

Для поддержки импорта модулей и инициализированных пакетов, а также для внесения вклада в пространства имён пакетов, искатели записей пути должны реализовывать метод find_spec().

find_spec() принимает два аргумента: полностью квалифицированное имя импортируемого модуля и (необязательный) целевой модуль. find_spec() возвращает полностью заполненную спецификацию для модуля. В этой спецификации всегда будет установлен «loader» (за одним исключением).

Чтобы сообщить механизму импорта, что спецификация представляет собой порцию пространства имён, поисковик пути устанавливает для спецификации значение «loader» равным None, а «submodule_search_locations» – списком, содержащим эту порцию.

Изменено в версии 3.4: find_spec() заменил find_loader() и find_module(); оба они теперь устарели, но будут использоваться, если find_spec() не определён.

Старые поисковики пути могут реализовывать один из двух устаревших методов вместо find_spec(). Эти методы по-прежнему учитываются ради обратной совместимости. Однако если find_spec() реализован в поисковике пути, старые методы игнорируются.

find_loader() принимает один аргумент – полное квалифицированное имя импортируемого модуля. find_loader() возвращает кортеж из двух элементов, где первый – загрузчик, а второй – порция пространства имён portion. Если первый элемент (т.е. загрузчик) равен None, это означает, что, хотя поисковик пути не имеет загрузчика для указанного модуля, он знает, что запись пути вносит вклад в порцию пространства имён для этого модуля. Это почти всегда будет тот случай, когда Python просят импортировать пакет пространства имён, не имеющий физического представления в файловой системе. Когда поисковик пути возвращает None в качестве загрузчика, второй элемент возвращаемого кортежа из двух элементов должен быть последовательностью, хотя может быть пустым.

Если find_loader() возвращает значение загрузчика, отличное от None, порция игнорируется, и загрузчик возвращается из поисковика на основе путей, завершая поиск по записям пути.

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

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

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

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

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

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

5.7. Особые замечания для __main__Special considerations for __main__

Модуль __main__ является особым случаем в системе импорта Python. Как отмечалось ранее, модуль __main__ инициализируется непосредственно при запуске интерпретатора, так же как sys и builtins. Однако, в отличие от них, он не в полной мере относится к встроенным модулям. Это связано с тем, что способ инициализации __main__ зависит от флагов и других параметров, с которыми был вызван интерпретатор.

5.7.1. __main__.__spec__

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

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

В остальных случаях __main__.__spec__ устанавливается в None, поскольку код, используемый для заполнения __main__, не соответствует напрямую импортируемому модулю:

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

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

Note also that even when __main__ corresponds with an importable module and __main__.__spec__ is set accordingly, they’re still considered distinct modules. This is due to the fact that blocks guarded by if __name__ == "__main__": checks only execute when the module is used to populate the __main__ namespace, and not during normal import.

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

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

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

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

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

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

5.9. СсылкиReferences

Механизм импорта значительно изменился с первых дней Python. Оригинальная спецификация пакетов всё ещё доступна для чтения, хотя некоторые детали изменились после написания этого документа.

Исходная спецификация sys.meta_path была PEP 302, с последующим расширением в PEP 420.

PEP 420 ввёл пакеты пространств имён для Python 3.3. PEP 420 также ввёл протокол find_loader() в качестве альтернативы find_module().

PEP 366 описывает добавление атрибута __package__ для явных относительных импортов в главных модулях.

PEP 328 ввёл абсолютные и явные относительные импорты и изначально предложил __name__ для семантики, которую PEP 366 в итоге определил для __package__.

PEP 338 определяет выполнение модулей как сценариев.

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

Сноски

[1]См. types.ModuleType.
[2]Реализация importlib избегает прямого использования возвращаемого значения. Вместо этого она получает объект модуля, выполняя поиск имени модуля в sys.modules. Косвенным следствием этого является то, что импортированный модуль может заменить себя в sys.modules. Это поведение, зависящее от реализации, которое не гарантируется в других реализациях Python.
[3]В устаревшем коде можно найти экземпляры imp.NullImporter в sys.path_importer_cache. Рекомендуется изменить код так, чтобы вместо этого использовался None. См. Перенос кода Python для получения дополнительных сведений.