Spec-Zone.ru › Python 3.11

Система импорта

Код 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 соответственно.

5.2.2. Пакеты с именным пространством

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

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

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

См. также PEP 420 для спецификации пакета с именным пространством.

5.3. Поиск

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

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

5.3.1. Кэш модулей

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

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

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

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

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

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

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

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

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

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

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

5.3.3. Плагины импорта

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

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

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

5.3.4. Метапуть

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

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

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

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

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

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

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

Изменено в версии 3.10: Использование find_module() системой импорта теперь вызывает ImportWarning.

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» в примере псевдокода выше), как суммировано в позже раздела.
  • Выполнение модуля — ключевой момент загрузки, в котором пространство имён модуля заполняется. Выполнение полностью делегировано загрузчику, который решает, что и как заполнять.
  • Модуль, созданный во время загрузки и переданный exec_module(), может не быть тем, который возвращается в конце импорта 2.

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

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

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

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

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

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

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

Новое в версии 3.4: Метод create_module() загрузчиков.

Изменено в версии 3.4: Метод load_module() был заменён на exec_module(), и механизм импорта взял на себя все рутинные обязанности загрузки.

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

Метод load_module() должен реализовывать всю рутинную функциональность загрузки, описанную выше, в дополнение к выполнению модуля. Применяются те же ограничения, с некоторыми дополнительными уточнениями:

  • Если в sys.modules уже существует объект модуля с данным именем, загрузчик должен использовать этот существующий модуль. (В противном случае importlib.reload() не будет работать правильно.) Если названного модуля нет в sys.modules, загрузчик должен создать новый объект модуля и добавить его в sys.modules.
  • Модуль должен существовать в sys.modules до выполнения кода модуля загрузчиком, чтобы предотвратить неограниченную рекурсию или повторную загрузку.
  • Если загрузка завершается неудачей, загрузчик должен удалить любые модули, которые он вставил в sys.modules, но он должен удалить только неудавшийся модуль(и), и только если сам загрузчик явно загрузил модуль(и).

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

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

Изменено в версии 3.10: Использование load_module() вызовет ImportWarning.

5.4.2. Подмодули

При загрузке подмодуля любым способом (например, через importlib API, операторы 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. Спецификация модуля

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

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

Спецификация модуля доступна в качестве атрибута __spec__ объекта модуля. Подробнее о содержимом спецификации модуля см. в ModuleSpec.

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

5.4.4. Атрибуты модулей, связанные с импортом

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

__name__

Атрибут __name__ должен быть установлен на полное квалифицированное имя модуля. Это имя используется для уникальной идентификации модуля в системе импорта.

__loader__

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

__package__

Атрибут __package__ модуля должен быть установлен. Его значение должно быть строкой, но оно может быть таким же, как и его __name__. Когда модуль является пакетом, его значение __package__ должно быть установлено на его значение __name__. Когда модуль не является пакетом, __package__ должен быть установлен на пустую строку для модулей верхнего уровня или для подмодулей на имя родительского пакета. Обратитесь к PEP 366 для получения дополнительной информации.

Этот атрибут используется вместо __name__ для вычисления явных относительных импортов для основных модулей, как определено в PEP 366. Ожидается, что он будет иметь то же значение, что и __spec__.parent.

Изменено в версии 3.6: Значение __package__ должно совпадать со значением __spec__.parent.

__spec__

Атрибут __spec__ должен быть установлен на спецификацию модуля, которая использовалась при импорте модуля. Установка __spec__ должным образом применяется ко всем модулям, инициализированным во время запуска интерпретатора. Единственное исключение — __main__, где __spec__ установлен в None в некоторых случаях.

Когда __package__ не определен, используется __spec__.parent в качестве резервного варианта.

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

Изменено в версии 3.6: __spec__.parent используется в качестве резервного варианта, когда __package__ не определен.

__path__

Если модуль является пакетом (обычным или пространством имён), атрибут __path__ объекта модуля должен быть установлен. Значение должно быть итерируемым, но может быть пустым, если __path__ не имеет дальнейшего значения. Если __path__ не пусто, при итерировании оно должно генерировать строки. Более подробная информация о семантике __path__ приведена ниже.

Модули, не являющиеся пакетами, не должны иметь атрибут __path__.

__file__
__cached__

__file__ необязательно (если задано, значение должно быть строкой). Оно указывает путь к файлу, из которого был загружен модуль (если загружен из файла), или путь к файлу общей библиотеки для модулей расширения, загруженных динамически из общей библиотеки. Он может отсутствовать для определённых типов модулей, таких как модули C, статически связанных с интерпретатором, и система импорта может выбрать не устанавливать его, если он не имеет семантического значения (например, модуль, загруженный из базы данных).

Если __file__ установлен, то может быть установлен и атрибут __cached__, который представляет собой путь к любой скомпилированной версии кода (например, байткодированному файлу). Файл не обязательно должен существовать для установки этого атрибута; путь может просто указывать на то, где должен находиться скомпилированный файл (см. PEP 3147).

Обратите внимание, что __cached__ может быть установлен, даже если __file__ не установлен. Однако такой сценарий довольно необычен. В конечном итоге загрузчик использует спецификацию модуля, предоставленную поисковиком (из которого __file__ и __cached__ получены). Таким образом, если загрузчик может загрузить модуль из кэша, но в противном случае не загружает его из файла, этот необычный сценарий может быть уместным.

5.4.5. module.__path__

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

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

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

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

5.4.6. Представления модулей

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

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

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

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

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

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

Изменено в версии 3.10: Вызов module_repr() теперь выполняется после попытки использовать атрибут модуля __spec__, но до возврата к использованию __file__. Использование module_repr() планируется прекратить в Python 3.12.

5.4.7. Невалидация кэшированного байткода

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

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

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

5.5. Поиск по пути

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

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

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

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

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

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

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

5.5.1. Поиск по пути

Класс path based finder отвечает за поиск и загрузку модулей и пакетов Python, чьи расположения заданы строкой path entry. Большинство path entries указывают на места в файловой системе, но не ограничиваются только этим.

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

Класс path based finder использует три переменные: sys.path, sys.path_hooks и sys.path_importer_cache. Также используются атрибуты __path__ объектов пакетов. Они предоставляют дополнительные способы настройки механизма импорта.

sys.path содержит список строк, указывающих на места поиска модулей и пакетов. Он инициализируется из переменной среды PYTHONPATH и различных других значений по умолчанию, специфичных для установки и реализации. Элементы в sys.path могут указывать на каталоги в файловой системе, zip-файлы и потенциально другие «места» (см. модуль site), которые должны быть просмотрены для поиска модулей, такие как URL-адреса или запросы к базам данных. В sys.path должны быть только строки; все другие типы данных игнорируются.

Класс path based finder является мета-поисковиком, поэтому механизм импорта начинает поиск по пути импорта, вызывая метод find_spec() класса path based finder, как описано ранее. Когда аргумент path к методу find_spec() задаётся, он будет списком путей строк для обхода — как правило, атрибутом __path__ пакета для импорта внутри этого пакета. Если аргумент path равен None, это указывает на импорт верхнего уровня, и используется sys.path.

Поисковик по пути итерируется по каждому элементу пути поиска, и для каждого из них ищет соответствующий поисковик по элементу пути (PathEntryFinder) для этого элемента пути. Поскольку это может быть дорогостоящей операцией (например, может быть накладные расходы на вызов stat() для этого поиска), поисковик по пути сохраняет кэш, сопоставляющий элементы пути с поисковиками по этим элементам пути. Этот кэш сохраняется в sys.path_importer_cache (несмотря на название, этот кэш фактически хранит объекты поисковиков, а не ограничен объектами импортёров). Таким образом, дорогостоящий поиск поисковика по элементу пути для конкретного местоположения элемента пути необходимо выполнить только один раз. Пользовательский код свободен удалять элементы из кэша sys.path_importer_cache, заставляя поисковик по пути выполнить поиск по элементу пути снова 3.

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

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

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

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

find_loader() принимает один аргумент — полное квалифицированное имя импортируемого модуля. find_loader() возвращает кортеж из двух элементов: загрузчик и часть пространства имен.

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

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

Изменено в версии 3.10: Вызовы find_module() и find_loader() системой импорта будут вызывать ImportWarning.

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

Примечания

1

См. types.ModuleType.

2

Реализация importlib избегает непосредственного использования возвращаемого значения. Вместо этого она получает объект модуля, выполняя поиск имени модуля в sys.modules. Непрямым следствием этого является то, что импортированный модуль может заменить себя в sys.modules. Это поведение, специфичное для реализации, не гарантируется в других реализациях Python.

3

В устаревшем коде возможно найти экземпляры imp.NullImporter в sys.path_importer_cache. Рекомендуется изменить код на использование None вместо этого. Более подробную информацию см. в Переносе кода Python.

© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/reference/import.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API