5. Система імпорту¶
Код Python в одному module отримує доступ до коду в іншому модулі шляхом процесу importing його. Інструкція 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 має концепцію packages.
Ви можете розглядати пакети як каталоги у файловій системі, а модулі як файли в каталогах, але не сприймайте цю аналогію надто буквально, оскільки пакети та модулі не обов’язково походять із файлової системи. Для цілей цієї документації ми будемо використовувати цю зручну аналогію каталогів і файлів. Як і каталоги файлової системи, пакунки організовані ієрархічно, і самі пакунки можуть містити підпакети, а також звичайні модулі.
Важливо мати на увазі, що всі пакети є модулями, але не всі модулі є пакетами. Або інакше кажучи, пакунки - це просто особливий вид модулів. Зокрема, будь-який модуль, який містить атрибут __path__
, вважається пакетом.
Усі модулі мають назву. Назви підпакетів відокремлюються від назви батьківського пакета крапкою, подібно до стандартного синтаксису доступу до атрибутів Python. Таким чином, у вас може бути пакет під назвою email
, який, у свою чергу, має підпакет під назвою email.mime
і модуль у цьому підпакеті під назвою email.mime.text
.
5.2.1. Звичайні пакети¶
Python визначає два типи пакетів: звичайні пакунки і пакети простору імен. Звичайні пакунки — це традиційні пакунки, які існували в Python 3.2 і раніше. Звичайний пакет зазвичай реалізується як каталог, що містить файл __init__.py
. Коли імпортується звичайний пакет, цей файл __init__.py
виконується неявно, а об’єкти, які він визначає, прив’язуються до імен у просторі імен пакета. Файл __init__.py
може містити той самий код Python, який може містити будь-який інший модуль, і Python додасть деякі додаткові атрибути до модуля під час його імпорту.
Наприклад, наведений нижче макет файлової системи визначає «батьківський» пакет верхнього рівня з трьома підпакетами:
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
. Насправді під час пошуку імпорту може бути знайдено кілька батьківських
каталогів, де кожен надається окремою частиною. Таким чином, батьківський/один
може фізично не знаходитися поруч із батьківським/два
. У цьому випадку Python створюватиме пакет простору імен для батьківського
пакета верхнього рівня кожного разу, коли він або один із його підпакетів імпортується.
Дивіться також 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. Цей протокол складається з двох концептуальних об’єктів, finders і loaders. Робота шукача полягає в тому, щоб визначити, чи зможе він знайти названий модуль, використовуючи ту стратегію, про яку він знає. Об’єкти, які реалізують обидва ці інтерфейси, називаються імпортерами — вони повертаються самі, коли виявляють, що можуть завантажити запитаний модуль.
Python містить ряд засобів пошуку та імпорту за замовчуванням. Перший знає, як знайти вбудовані модулі, а другий знає, як знайти заморожені модулі. Третій засіб пошуку за умовчанням шукає модулі в шляху імпорту. шлях імпорту — це список розташувань, які можуть іменувати шляхи файлової системи або файли zip. Його також можна розширити для пошуку будь-якого ресурсу, який можна знайти, наприклад, ідентифікованого за URL-адресами.
Механізм імпорту можна розширювати, тому можна додавати нові шукачі, щоб розширити діапазон і обсяг пошуку модулів.
Шукачі фактично не завантажують модулі. Якщо їм вдається знайти названий модуль, вони повертають module spec, інкапсуляцію інформації, пов’язаної з імпортом модуля, яку потім використовує механізм імпорту під час завантаження модуля.
У наступних розділах більш детально описано протоколи для шукачів і завантажувачів, зокрема те, як ви можете створити та зареєструвати нові протоколи для розширення механізму імпорту.
Змінено в версії 3.4: У попередніх версіях Python шукачі повертали loaders безпосередньо, тоді як тепер вони повертають специфікації модулів, які містять завантажувачі. Завантажувачі все ще використовуються під час імпорту, але мають менше обов’язків.
5.3.3. Імпортні гачки¶
Імпортна техніка розрахована на розширення; основним механізмом для цього є перехоплення імпорту. Існує два типи хуків імпорту: мета-хуки та хуки шляху імпорту.
Мета-хуки викликаються на початку обробки імпорту, до того, як відбулася будь-яка інша обробка імпорту, окрім пошуку кешу sys.modules
. Це дозволяє мета-хукам перевизначати sys.path
обробку, заморожені модулі або навіть вбудовані модулі. Мета-хуки реєструються шляхом додавання нових об’єктів пошуку до sys.meta_path
, як описано нижче.
Перехоплювачі шляхів імпорту викликаються як частина обробки sys.path
(або package.__path__
) у точці, де зустрічається пов’язаний з ними елемент шляху. Хуки шляхів імпорту реєструються шляхом додавання нових викликів до sys.path_hooks
, як описано нижче.
5.3.4. Меташлях¶
When the named module is not found in sys.modules
, Python next
searches sys.meta_path
, which contains a list of meta path finder
objects. These finders are queried in order to see if they know how to handle
the named module. Meta path finders must implement a method called
find_spec()
which takes three arguments:
a name, an import path, and (optionally) a target module. The meta path
finder can use any strategy it wants to determine whether it can handle
the named module or not.
Якщо засіб пошуку меташляху знає, як обробляти названий модуль, він повертає специфікаційний об’єкт. Якщо він не може обробити названий модуль, він повертає None
. Якщо обробка sys.meta_path
досягає кінця свого списку без повернення специфікації, тоді виникає ModuleNotFoundError
. Будь-які інші викликані винятки просто поширюються, перериваючи процес імпорту.
The find_spec()
method of meta path
finders is called with two or three arguments. The first is the fully
qualified name of the module being imported, for example foo.bar.baz
.
The second argument is the path entries to use for the module search. For
top-level modules, the second argument is None
, but for submodules or
subpackages, the second argument is the value of the parent package’s
__path__
attribute. If the appropriate __path__
attribute cannot
be accessed, a ModuleNotFoundError
is raised. The third argument
is an existing module object that will be the target of loading later.
The import system passes in a target module only during reload.
Мета-шлях може проходити кілька разів для одного запиту на імпорт. Наприклад, якщо припустити, що жоден із задіяних модулів уже не кешовано, імпорт 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 має три засоби пошуку меташляху: один, який знає, як імпортувати вбудовані модулі, інший, який знає, як імпортувати заморожені модулі, і інший, який знає, як імпортувати модулі зі шляху імпорту (import path, тобто path based finder).
Змінено в версії 3.4: The find_spec()
method of meta path
finders replaced find_module()
, which
is now deprecated. While it will continue to work without change, the
import machinery will try it only if the finder does not implement
find_spec()
.
Змінено в версії 3.10: Use of find_module()
by the import system
now raises ImportWarning
.
Змінено в версії 3.12: find_module()
has been removed.
Use find_spec()
instead.
5.4. Завантаження¶
Якщо специфікація модуля буде знайдена, механізм імпорту використовуватиме її (і завантажувач, який вона містить) під час завантаження модуля. Ось приблизно те, що відбувається під час завантаження частини імпорту:
module = None
if spec.loader is not None and hasattr(spec.loader, 'create_module'):
# It is assumed 'exec_module' will also be defined on the loader.
module = spec.loader.create_module(spec)
if module is None:
module = ModuleType(spec.name)
# The import-related module attributes get set here:
_init_module_attrs(spec, module)
if spec.loader is None:
# unsupported
raise ImportError
if spec.origin is None and spec.submodule_search_locations is not None:
# namespace package
sys.modules[spec.name] = module
elif not hasattr(spec.loader, 'exec_module'):
module = spec.loader.load_module(spec.name)
else:
sys.modules[spec.name] = module
try:
spec.loader.exec_module(module)
except BaseException:
try:
del sys.modules[spec.name]
except KeyError:
pass
raise
return sys.modules[spec.name]
Зверніть увагу на такі деталі:
Якщо в
sys.modules
є існуючий об’єкт модуля з вказаною назвою, імпортування його вже повернуло.Модуль існуватиме в
sys.modules
до того, як завантажувач виконає код модуля. Це важливо, оскільки код модуля може (прямо чи опосередковано) імпортувати сам себе; додавання його доsys.modules
заздалегідь запобігає необмеженій рекурсії в гіршому випадку та багаторазовому завантаженню в найкращому.Якщо завантаження не вдасться, модуль із помилкою – і тільки модуль із помилкою – видаляється з
sys.modules
. Будь-який модуль, який уже знаходиться в кешіsys.modules
, і будь-який модуль, який було успішно завантажено як побічний ефект, повинні залишатися в кеші. Це контрастує з перезавантаженням, коли навіть несправний модуль залишається вsys.modules
.Після створення модуля, але перед його виконанням, механізм імпорту встановлює пов’язані з імпортом атрибути модуля («_init_module_attrs» у наведеному вище прикладі псевдокоду), як підсумовано в пізнішому розділі.
Виконання модуля є ключовим моментом завантаження, під час якого простір імен модуля заповнюється. Виконання повністю делеговано завантажувачу, який вирішує, що заповнювати і як.
Модуль, створений під час завантаження та переданий у exec_module(), може не бути тим, що повертається в кінці імпорту [2].
Змінено в версії 3.4: Система імпорту взяла на себе шаблонні обов’язки вантажників. Раніше вони виконувалися методом importlib.abc.Loader.load_module()
.
5.4.1. Вантажники¶
Завантажувачі модулів забезпечують критичну функцію завантаження: виконання модуля. Механізм імпорту викликає метод importlib.abc.Loader.exec_module()
з одним аргументом, об’єктом модуля, який потрібно виконати. Будь-яке значення, повернуте з exec_module()
ігнорується.
Вантажники повинні відповідати таким вимогам:
Якщо модуль є модулем Python (на відміну від вбудованого модуля або динамічно завантажуваного розширення), завантажувач повинен виконати код модуля в глобальному просторі імен модуля (
module.__dict__
).Якщо завантажувач не може виконати модуль, він має викликати
ImportError
, хоча будь-які інші винятки, викликані під часexec_module()
, будуть поширені.
У багатьох випадках шукач і завантажувач можуть бути одним і тим же об’єктом; у таких випадках метод find_spec()
просто повертатиме специфікацію із завантажувачем, встановленим на self
.
Завантажувачі модулів можуть вибрати створення об’єкта модуля під час завантаження, реалізувавши метод create_module()
. Він приймає один аргумент, специфікацію модуля, і повертає новий об’єкт модуля для використання під час завантаження. create_module()
не потребує встановлення жодних атрибутів для об’єкта модуля. Якщо метод повертає None
, механізм імпорту створить новий модуль сам.
Added in version 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
, але він повинен видалити лише модуль(и) з помилкою, і лише якщо завантажувач сам завантажив модуль( s) явно.
Змінено в версії 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. Module specs¶
Механізм імпорту використовує різноманітну інформацію про кожен модуль під час імпорту, особливо перед завантаженням. Більшість інформації є спільною для всіх модулів. Метою специфікації модуля є інкапсуляція цієї інформації, пов’язаної з імпортом, на основі кожного модуля.
Використання специфікації під час імпорту дозволяє передавати стан між компонентами системи імпорту, наприклад. між шукачем, який створює специфікацію модуля, і завантажувачем, який його виконує. Найважливіше те, що це дозволяє імпортним механізмам виконувати стандартні операції завантаження, тоді як без специфікації модуля цю відповідальність несе завантажувач.
The module’s spec is exposed as module.__spec__
. Setting
__spec__
appropriately applies equally to
modules initialized during interpreter startup.
The one exception is __main__
, where __spec__
is
set to None in some cases.
See ModuleSpec
for details on the contents of
the module spec.
Added in version 3.4.
5.4.4. __path__ attributes on modules¶
The __path__
attribute should be a (possibly empty)
sequence of strings enumerating the locations where the package’s
submodules will be found. By definition, if a module has a __path__
attribute, it is a package.
A package’s __path__
attribute is used during imports of its
subpackages.
Within the import machinery, it functions much the same as sys.path
,
i.e. providing a list of locations to search for modules during import.
However, __path__
is typically much more constrained than
sys.path
.
The same rules used for sys.path
also apply to a package’s
__path__
. sys.path_hooks
(described below) are
consulted when traversing a package’s __path__
.
A package’s __init__.py
file may set or alter the package’s
__path__
attribute, and this was typically the way namespace packages were implemented
prior to PEP 420. With the adoption of PEP 420, namespace packages no
longer need to supply __init__.py
files containing only __path__
manipulation code; the import machinery automatically sets __path__
correctly for the namespace package.
5.4.5. Репрезентація модуля¶
За замовчуванням усі модулі мають придатне для використання відображення, однак, залежно від атрибутів, налаштованих вище, і в специфікації модуля ви можете чіткіше керувати відтворенням об’єктів модуля.
Якщо модуль має специфікацію (__spec__
), механізм імпорту спробує згенерувати з неї repr. Якщо це не вдається або немає специфікації, система імпорту створить відображення за замовчуванням, використовуючи будь-яку інформацію, доступну в модулі. Він намагатиметься використати module.__name__
, module.__file__
і module.__loader__
як вхідні дані в repr, із значеннями за замовчуванням для будь-якої інформації, якої немає.
Ось точні правила, які використовуються:
Якщо модуль має атрибут
__spec__
, інформація в специфікації використовується для створення repr. Переглядаються атрибути «name», «loader», «origin» і «has_location».Якщо модуль має атрибут
__file__
, він використовується як частина repr модуля.Якщо модуль не має
__file__
, але має__loader__
, який не єNone
, тоді repr завантажувача використовується як частина repr модуля.В іншому випадку просто використовуйте
__name__
модуля в repr.
Змінено в версії 3.12: Use of module_repr()
, having been deprecated since Python 3.4, was
removed in Python 3.12 and is no longer called during the resolution of a
module’s repr.
5.4.6. Анулювання кешованого байт-коду¶
Перед тим, як Python завантажить кешований байт-код із файлу .pyc
, він перевіряє, чи оновлений кеш із вихідним файлом .py
. За замовчуванням Python робить це, зберігаючи мітку часу останньої зміни джерела та розмір у файлі кешу під час його запису. Під час виконання система імпорту потім перевіряє кеш-файл, перевіряючи збережені в кеш-файлі метадані з метаданими джерела.
Python також підтримує кеш-файли на основі хешів, які зберігають хеш вмісту вихідного файлу, а не його метаданих. Існує два варіанти хеш-файлів .pyc
: позначений і не позначений. Для перевірених хеш-файлів .pyc
Python перевіряє файл кешу, хешуючи вихідний файл і порівнюючи отриманий хеш із хешем у файлі кешу. Якщо перевірений файл кешу на основі хешу виявляється недійсним, Python повторно генерує його та записує новий перевірений файл кешу на основі хешу. Для неперевірених файлів .pyc
на основі хешу Python просто припускає, що файл кешу дійсний, якщо він існує. Поведінка перевірки файлів .pyc
на основі хешу може бути замінена прапором --check-hash-based-pycs
.
Змінено в версії 3.7: Додано файли .pyc
на основі хешу. Раніше Python підтримував лише недійсність кешів байт-кодів на основі часових позначок.
5.5. Пошук на основі шляху¶
Як згадувалося раніше, Python поставляється з декількома типовими засобами пошуку меташляхів. Один із них, який називається пошук на основі шляху (PathFinder
), шукає шлях імпорту, який містить список записів шляху. Кожен запис шляху називає розташування для пошуку модулів.
Сам шукач на основі шляху не знає, як нічого імпортувати. Замість цього він проходить окремі записи шляху, пов’язуючи кожен із них із інструментом пошуку записів шляху, який знає, як обробляти цей конкретний шлях.
Стандартний набір засобів пошуку шляхів реалізує всю семантику для пошуку модулів у файловій системі, обробки спеціальних типів файлів, таких як вихідний код Python (файли .py
), байт-код Python (файли .pyc
) і спільні бібліотеки (наприклад, файли .so
). Якщо підтримується модулем zipimport
у стандартній бібліотеці, засоби пошуку шляхів за замовчуванням також обробляють завантаження всіх цих типів файлів (окрім спільних бібліотек) із файлів zip.
Записи шляху не повинні обмежуватися розташуванням файлової системи. Вони можуть посилатися на URL-адреси, запити до бази даних або будь-яке інше розташування, яке можна вказати як рядок.
Шукач на основі шляху надає додаткові перехоплювачі та протоколи, щоб ви могли розширювати та налаштовувати типи записів шляху для пошуку. Наприклад, якщо ви хочете підтримувати записи шляху як мережеві URL-адреси, ви можете написати хук, який реалізує семантику HTTP для пошуку модулів в Інтернеті. Цей хук (об’єкт виклику) повертав би path entry finder, що підтримує описаний нижче протокол, який потім використовувався для отримання завантажувача для модуля з Інтернету.
Попередження: у цьому та попередньому розділах використовується термін finder, розрізняючи їх за допомогою термінів meta path finder і path entry finder. Ці два типи шукачів дуже схожі, підтримують схожі протоколи та функціонують однаково під час процесу імпорту, але важливо мати на увазі, що вони дещо відрізняються. Зокрема, шукачі меташляху працюють на початку процесу імпорту, як ключ від обходу sys.meta_path
.
Навпаки, засоби пошуку запису шляху є в певному сенсі деталлю реалізації засобу пошуку шляху, і фактично, якби засіб пошуку шляху було вилучено з sys.meta_path
, жодна семантика засобу пошуку шляху не буде викликатися.
5.5.1. Шукачі входу в шлях¶
path based finder відповідає за пошук і завантаження модулів і пакетів Python, розташування яких вказано за допомогою рядка path entry. Більшість записів шляхів іменують розташування у файловій системі, але вони не повинні обмежуватися цим.
Як мета-пошук шляху, пошук на основі шляху реалізує find_spec()
протокол, описаний раніше, однак він надає додаткові перехоплювачі, які можна використовувати для налаштування способу пошуку модулів і завантажується з шляху імпорту.
Три змінні використовуються path based finder, sys.path
, sys.path_hooks
і sys.path_importer_cache
. Також використовуються атрибути __path__
об’єктів пакета. Це надає додаткові способи налаштування імпортного обладнання.
sys.path
contains a list of strings providing search locations for
modules and packages. It is initialized from the PYTHONPATH
environment variable and various other installation- and
implementation-specific defaults. Entries in sys.path
can name
directories on the file system, zip files, and potentially other «locations»
(see the site
module) that should be searched for modules, such as
URLs, or database queries. Only strings should be present on
sys.path
; all other data types are ignored.
пошук на основі шляху є метапошуком шляху, тому механізм імпорту починає пошук шляху імпорту, викликаючи Коли вказано аргумент ``path`()
для find_spec()
, це буде список шляхів до рядків, які потрібно пройти - зазвичай атрибут __path__
пакета для імпорту в цьому пакет. Якщо аргумент path
має значення None
, це вказує на імпорт верхнього рівня та sys.path
використовується.
The path based finder iterates over every entry in the search path, and
for each of these, looks for an appropriate path entry finder
(PathEntryFinder
) for the
path entry. Because this can be an expensive operation (e.g. there may be
stat()
call overheads for this search), the path based finder maintains
a cache mapping path entries to path entry finders. This cache is maintained
in sys.path_importer_cache
(despite the name, this cache actually
stores finder objects rather than being limited to importer objects).
In this way, the expensive search for a particular path entry
location’s path entry finder need only be done once. User code is
free to remove cache entries from sys.path_importer_cache
forcing
the path based finder to perform the path entry search again.
Якщо запису шляху немає в кеші, пошук на основі шляху повторює кожен виклик у sys.path_hooks
. Кожен із перехоплювачів запису шляху у цьому списку викликається з одним аргументом, записом шляху, який потрібно шукати. Цей виклик може повертати пошук запису шляху, який може обробити запис шляху, або може викликати ImportError
. Помилка ImportError
використовується шукачем на основі шляху, щоб сигналізувати про те, що хук не може знайти пошук запису шляху для цього запису шляху. Виняток ігнорується, і повторення шляху імпорту продовжується. Хук повинен очікувати об’єкт string або bytes; кодування об’єктів bytes залежить від хука (наприклад, це може бути кодування файлової системи, UTF-8 або щось інше), і якщо хук не може декодувати аргумент, він має викликати ImportError
.
Якщо sys.path_hooks
ітерація закінчується без повернення path entry finder, тоді метод пошуку на основі шляху find_spec()
зберігатиме None
у sys.path_importer_cache
(щоб вказати, що немає засобу пошуку для цього запису шляху) і повернути None
, вказуючи, що цей meta path finder не зміг знайти модуль.
Якщо path entry finder повертається одним із викликів path entry hook на sys.path_hooks
, тоді використовується наступний протокол, щоб запитати модуль на пошуковик spec, який потім використовується під час завантаження модуля.
Поточний робочий каталог, позначений порожнім рядком, обробляється дещо інакше, ніж інші записи в sys.path
. По-перше, якщо поточний робочий каталог не існує, жодне значення не зберігається в sys.path_importer_cache
. По-друге, значення для поточного робочого каталогу оновлюється для кожного пошуку модуля. По-третє, шлях, який використовується для sys.path_importer_cache
і повертається importlib.machinery.PathFinder.find_spec()
, буде фактичним поточним робочим каталогом, а не порожнім рядком.
5.5.2. Протокол пошуку входу шляху¶
Для того, щоб підтримувати імпорт модулів та ініціалізованих пакетів, а також вносити частини до пакетів простору імен, засоби пошуку запису шляху повинні реалізувати метод find_spec()
.
find_spec()
приймає два аргументи: повне ім’я модуля, який імпортується, і (необов’язковий) цільовий модуль. find_spec()
повертає повністю заповнену специфікацію для модуля. Ця специфікація завжди матиме встановлений «завантажувач» (за одним винятком).
To indicate to the import machinery that the spec represents a namespace
portion, the path entry finder sets submodule_search_locations
to
a list containing the portion.
Змінено в версії 3.4: find_spec()
replaced
find_loader()
and
find_module()
, both of which
are now deprecated, but will be used if find_spec()
is not defined.
Старіші засоби пошуку запису шляху можуть використовувати один із цих двох застарілих методів замість find_spec()
. Методи досі поважаються заради зворотної сумісності. Однак, якщо find_spec()
реалізовано в пошуку запису шляху, застарілі методи ігноруються.
find_loader()
takes one argument, the
fully qualified name of the module being imported. find_loader()
returns a 2-tuple where the first item is the loader and the second item
is a namespace portion.
Для зворотної сумісності з іншими реалізаціями протоколу імпорту багато засобів пошуку шляхів також підтримують той самий традиційний метод find_module()
, який підтримують засоби пошуку меташляхів. Однак методи Find_module() засобу пошуку записів шляху ніколи не викликаються з аргументом path
(вони мають записати відповідну інформацію про шлях від початкового виклику до перехоплення шляху).
Метод find_module()
для засобів пошуку записів шляху застарів, оскільки він не дозволяє засобу пошуку записів шляху вносити частини до пакетів простору імен. Якщо і «find_loader()», і «find_module()» існують у пошуку запису шляху, система імпорту завжди викличе «find_loader()», а не «find_module()».
Змінено в версії 3.10: Calls to find_module()
and
find_loader()
by the import
system will raise ImportWarning
.
Змінено в версії 3.12: find_module()
and find_loader()
have been removed.
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
працює з stdin
працює безпосередньо з джерела або файлу байт-коду
Зауважте, що __main__.__spec__
завжди має значення None
в останньому випадку, навіть якщо технічно файл можна імпортувати безпосередньо як модуль. Використовуйте перемикач -m
, якщо потрібні дійсні метадані модуля в __main__
.
Зауважте також, що навіть якщо __main__
відповідає імпортованому модулю, а __main__.__spec__
встановлено відповідно, вони все одно вважаються окремими модулями. Це пов’язано з тим, що блоки, які захищаються перевірками if __name__ == "__main__":
, виконуються лише тоді, коли модуль використовується для заповнення простору імен __main__
, а не під час звичайного імпорту.
5.9. Список літератури¶
Механізм імпорту значно розвинувся з перших днів Python. Оригінальна специфікація пакетів все ще доступна для читання, хоча деякі деталі змінилися з моменту написання цього документа.
Початкова специфікація для sys.meta_path
була PEP 302 з подальшим розширенням у PEP 420.
PEP 420 introduced namespace packages for
Python 3.3. PEP 420 also introduced the find_loader()
protocol as an
alternative to find_module()
.
PEP 366 описує додавання атрибута __package__
для явного відносного імпорту в основні модулі.
PEP 328 ввів абсолютний і явний відносний імпорт і спочатку запропонував __name__
для семантики PEP 366 згодом вказав би для __package__
.
PEP 338 визначає модулі виконання як скрипти.
PEP 451 додає інкапсуляцію стану імпорту кожного модуля в специфікаційні об’єкти. Це також перекладає більшість шаблонних обов’язків вантажників назад на імпортну техніку. Ці зміни дозволяють припинити підтримку кількох API у системі імпорту, а також додати нові методи до засобів пошуку та завантаження.
Виноски