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’s. Таким образом, вы можете иметь модуль под названием 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, а затем повторно импортировать именованный модуль, два объекта модуля not будут одинаковыми. В отличие от этого, importlib.reload() повторно использует объект модуля same и просто повторно инициализирует содержимое модуля путем повторного запуска код модуля.

5.3.2. Поисковики и загрузчики

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

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

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

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

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

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

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

Импортный механизм рассчитан на растяжимость; основным механизмом для этого являются хуки импорта. Существует два типа импортных крючков: мета хуки и хуки путей импорта.

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

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

5.3.4. Мета путь

Если именованный модуль не найден в sys.modules, Python далее выполняет поиск sys.meta_path, который содержит список объектов metapath finder. Эти средства поиска запрашиваются, чтобы узнать, умеют ли они обрабатывать именованный модуль. Искатели пути меты должны осуществить метод по имени 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 будет импортирован, пересекая meta путь во второй раз, называя 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’s по умолчанию есть три meta искателя пути, тот, который знает, как импортировать встроенные модули, тот, который знает, как импортировать замороженные модули и тот, который знает, как импортировать модули из пути импорта (т.е. поисковик на основе пути).

Изменено в версии 3.4: find_spec() метод мета-поисковиков заменен 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)
    # Set __loader__ and __package__ if missing.
else:
    sys.modules[spec.name] = module
    try:
        spec.loader.exec_module(module)
    except BaseException:
        try:
            del sys.modules[spec.name]
        except KeyError:
            pass
        raise
return sys.modules[spec.name]

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

  • Если существует объект модуля с заданным именем в sys.modules, импорт уже вернет его.
  • Модуль будет существовать в sys.modules, прежде чем загрузчик выполнит модуль код. Это крайне важно, потому что модуль код может (прямо или косвенно) импортировать себя; добавление его в sys.modules заранее предотвращает неограниченную рекурсию в худшем случае и многократную загрузку в лучшем.
  • Если погрузка терпит неудачу, провальный модуль - и только провальный модуль - удален из sys.modules. Любой модуль, уже находящийся в кэше sys.modules, и любой модуль, успешно загруженный как побочный эффект, должен оставаться в кэше. Это контрастирует с перезагрузкой, когда даже отказавший модуль остается в sys.modules.
  • После того, как модуль создан, но перед выполнением, оборудование импорта устанавливает связанные с импортом признаки модуля (» _init_module_attrs» в pseudo-код примере выше), как получено в итоге в последующем разделе.
  • Выполнение модуля - ключевой момент загрузки, в который заполняется пространство имен модуля. Выполнение полностью делегируется загрузчику, который решает, что заполняется и как.
  • Модуль, созданный во время загрузки и переданный в 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.
  • Модуль must существует в sys.modules, прежде чем загрузчик выполнит код модуля, чтобы предотвратить неограниченную рекурсию или многократную загрузку.
  • Если погрузка терпит неудачу, погрузчик должен удалить любые модули, которые это вставило в sys.modules, но это должно удалить only провальный модуль (модули), и только если сам погрузчик загрузил модуль (модули) явно.

Изменено в версии 3.5: DeprecationWarning возникает, когда exec_module() определен, а create_module() нет.

Изменено в версии 3.6: ImportError возникает, когда exec_module() определен, а create_module() нет.

5.4.2. Подмодули

Когда подмодуль загружается с использованием любого механизма (например, importlib API, операторов 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’s биндинг управляет этим, могло бы казаться удивительным, но это - на самом деле фундаментальная особенность системы импорта. Инвариантное удержание заключается в том, что если у вас есть sys.modules['spam'] и sys.modules['spam.foo'] (как было бы после вышеуказанного импорта), то последний должен отображаться как атрибут foo первого.

5.4.3. spec модуля

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

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

Спецификация модуля отображается как атрибут __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__ соответствующим образом применяется в равной степени к modules initialized during interpreter startup. Единственным исключением является __main__, где __spec__ является set to None in some cases.

Когда __package__ не определен, __spec__.parent - используемый как отступление.

Добавлено в версии 3.4.

Изменено в версии 3.6: __spec__.parent is used as a fallback when __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 модуля

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

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

Вот точные правила, используемые:

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

Изменено в версии 3.4: Использование loader.module_repr() было запрещено и спекуляция модуля - теперь используемый оборудованием импорта, чтобы произвести модуль repr.

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

5.4.7. Инвалидация кэша байткода

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

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

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

5.5. Поисковик на основе пути

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

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

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

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

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

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

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

5.5.1. Поисковики пути входа

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

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

Если поисковик пути входа, возвращен одним из хуком пути входа callables на 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() возвращает полностью заполненную спецификацию для модуля. В этой спецификации всегда будет установлен «загрузчик» (за одним исключением).

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

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

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

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

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

Для обратной совместимости с другими реализациями протокола импорта многие поисковики записей путей также поддерживают те же самые традиционные 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

должно представлять выражение как пригодное для использования, но .mod XXX.YYY.ZZZ Y не является допустимым выражением.

5.8. Особое рассмотрение __main__

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

5.8.1. __main__.__spec__

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

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

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

  • интерактивное приглашение
  • опция -c
  • запуск из stdin
  • запуск непосредственно из источника или bytecode файла

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

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

5.9. Нерешённые вопросы

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

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

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

XXX добавляют больше объяснения относительно различных путей, которыми инициализирован __main__?

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

5.10. Ссылки

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

Первоначальная спецификация для 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.