extension-modules.md
1> **Источник:** https://python-all.ru/3/c-api/extension-modules.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Определение модулей расширения89Расширение для CPython на C – это динамическая библиотека (например, файл `.so` в Linux, `.pyd` DLL в Windows), которая может быть загружена в процесс Python (например, скомпилирована с совместимыми настройками компилятора) и экспортирует [функцию инициализации](https://python-all.ru/3/c-api/extension-modules.html#extension-export-hook).1011Чтобы библиотека импортировалась по умолчанию (то есть с помощью [`importlib.machinery.ExtensionFileLoader`](https://python-all.ru/3/library/importlib.html#importlib.machinery.ExtensionFileLoader)), она должна быть доступна в [`sys.path`](https://python-all.ru/3/library/sys.html#sys.path), и её имя должно соответствовать имени модуля с расширением, указанным в [`importlib.machinery.EXTENSION_SUFFIXES`](https://python-all.ru/3/library/importlib.html#importlib.machinery.EXTENSION_SUFFIXES).1213> **Примечание**14>15> Сборку, упаковку и распространение модулей расширения лучше выполнять с помощью сторонних инструментов, и это выходит за рамки данного документа. Подходящим инструментом является Setuptools, документация которого находится по адресу [https://setuptools.pypa.io/en/latest/setuptools.html](https://python-all.ru/3/c-api/extension-modules.html).1617Обычно функция инициализации возвращает определение модуля, инициализированное с помощью [`PyModuleDef_Init()`](https://python-all.ru/3/c-api/extension-modules.html#c.PyModuleDef_Init). Это позволяет разбить процесс создания на несколько этапов:1819- До выполнения какого-либо значимого кода Python может определить, какие возможности поддерживает модуль, и может скорректировать окружение или отказаться от загрузки несовместимого расширения.20- По умолчанию Python сам создаёт объект модуля – то есть делает то же самое, что [`object.__new__()`](https://python-all.ru/3/reference/datamodel.html#object.__new__) для классов. Также он устанавливает начальные атрибуты, такие как [`__package__`](https://python-all.ru/3/reference/datamodel.html#module.__package__) и [`__loader__`](https://python-all.ru/3/reference/datamodel.html#module.__loader__).21- Затем объект модуля инициализируется с помощью кода, специфичного для расширения – аналога [`__init__()`](https://python-all.ru/3/reference/datamodel.html#object.__init__) для классов.2223Это называется *многофазной инициализацией*, чтобы отличать её от устаревшей (но всё ещё поддерживаемой) схемы *однофазной инициализации*, при которой функция инициализации возвращает полностью сконструированный модуль. Подробнее см. [раздел об однофазной инициализации ниже](https://python-all.ru/3/c-api/extension-modules.html#single-phase-initialization).2425Changed in version 3.5: Added support for multi-phase initialization ([**PEP 489**](https://python-all.ru/3/c-api/extension-modules.html)).2627## Несколько экземпляров модуля2829По умолчанию модули расширения не являются синглтонами. Например, если удалить запись [`sys.modules`](https://python-all.ru/3/library/sys.html#sys.modules) и повторно импортировать модуль, создаётся новый объект модуля, который обычно наполняется новыми объектами методов и типов. Старый модуль подлежит обычной сборке мусора. Такое поведение аналогично поведению модулей на чистом Python.3031Additional module instances may be created in [sub-interpreters](https://python-all.ru/3/c-api/subinterpreters.html#sub-interpreter-support) or after Python runtime reinitialization ([`Py_Finalize()`](https://python-all.ru/3/c-api/interp-lifecycle.html#c.Py_Finalize) and [`Py_Initialize()`](https://python-all.ru/3/c-api/interp-lifecycle.html#c.Py_Initialize)). In these cases, sharing Python objects between module instances would likely cause crashes or undefined behavior.3233To avoid such issues, each instance of an extension module should be *isolated*: changes to one instance should not implicitly affect the others, and all state owned by the module, including references to Python objects, should be specific to a particular module instance. See [Isolating Extension Modules](https://python-all.ru/3/howto/isolating-extensions.html#isolating-extensions-howto) for more details and a practical guide.3435A simpler way to avoid these issues is [raising an error on repeated initialization](https://python-all.ru/3/howto/isolating-extensions.html#isolating-extensions-optout).3637All modules are expected to support [sub-interpreters](https://python-all.ru/3/c-api/subinterpreters.html#sub-interpreter-support), or otherwise explicitly signal a lack of support. This is usually achieved by isolation or blocking repeated initialization, as above. A module may also be limited to the main interpreter using the [`Py_mod_multiple_interpreters`](https://python-all.ru/3/c-api/module.html#c.Py_mod_multiple_interpreters) slot.3839## Функция инициализации4041Функция инициализации, определённая модулем расширения, имеет следующую сигнатуру:4243#### `PyObject *PyInit_modulename(void)`4445Её имя должно быть `PyInit_<name>`, где `<name>` заменяется на имя модуля.4647Для модулей с именами, состоящими только из ASCII, функция должна называться `PyInit_<name>`, где `<name>` заменяется на имя модуля. При использовании [многофазной инициализации](https://python-all.ru/3/c-api/extension-modules.html#multi-phase-initialization) допускаются не-ASCII имена модулей. В этом случае имя функции инициализации – `PyInitU_<name>`, где `<name>` кодируется с помощью кодировки Python *punycode*, а дефисы заменяются символами подчёркивания. В Python:4849```python50def initfunc_name(name):51 try:52 suffix = b'_' + name.encode('ascii')53 except UnicodeEncodeError:54 suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')55 return b'PyInit' + suffix56```5758Рекомендуется определять функцию инициализации с помощью вспомогательного макроса:5960#### `PyMODINIT_FUNC`6162Объявляет функцию инициализации модуля расширения. Этот макрос:6364- задаёт возвращаемый тип [PyObject](https://python-all.ru/3/c-api/structures.html#c.PyObject)\*,65- добавляет любые специальные объявления компоновки, требуемые платформой, и66- для C++ объявляет функцию как `extern "C"`.6768Например, модуль с именем `spam` был бы определён так:6970```c71static struct PyModuleDef spam_module = {72 .m_base = PyModuleDef_HEAD_INIT,73 .m_name = "spam",74 ...75};7677PyMODINIT_FUNC78PyInit_spam(void)79{80 return PyModuleDef_Init(&spam_module);81}82```8384Можно экспортировать несколько модулей из одной динамической библиотеки, определив несколько функций инициализации. Однако для их импорта требуются символические ссылки или собственный импортёр, поскольку по умолчанию находится только функция, соответствующая имени файла. Подробнее см. раздел [Multiple modules in one library](https://python-all.ru/3/c-api/extension-modules.html) в [**PEP 489**](https://python-all.ru/3/c-api/extension-modules.html).8586Функция инициализации обычно является единственным элементом, не являющимся `static`, определённым в C-исходном коде модуля.8788## Многофазная инициализация8990Обычно [функция инициализации](https://python-all.ru/3/c-api/extension-modules.html#extension-export-hook) (`PyInit_modulename`) возвращает экземпляр [`PyModuleDef`](https://python-all.ru/3/c-api/module.html#c.PyModuleDef) с не-`NULL` [`m_slots`](https://python-all.ru/3/c-api/module.html#c.PyModuleDef.m_slots). Перед возвратом экземпляр `PyModuleDef` должен быть инициализирован с помощью следующей функции:9192#### `PyObject *PyModuleDef_Init(PyModuleDef *def)`9394*Часть [Stable ABI](https://python-all.ru/3/c-api/stable.html#stable) начиная с версии 3.5.*9596Гарантирует, что определение модуля является правильно инициализированным объектом Python, который корректно сообщает свой тип и счетчик ссылок.9798Возвращает *def*, приведённый к `PyObject*`, или `NULL` в случае ошибки.99100Вызов этой функции требуется для [многофазной инициализации](https://python-all.ru/3/c-api/extension-modules.html#multi-phase-initialization). Она не должна использоваться в других контекстах.101102Обратите внимание, что Python предполагает, что структуры `PyModuleDef` выделены статически. Эта функция может вернуть как новую ссылку, так и заимствованную; эту ссылку нельзя освобождать.103104Добавлено в версии 3.5.105106## Устаревшая однофазная инициализация107108> **Внимание**109>110> Однофазная инициализация – это устаревший механизм инициализации модулей расширения с известными недостатками и ошибками проектирования. Авторам модулей расширения рекомендуется вместо этого использовать многофазную инициализацию.111112При однофазной инициализации [функция инициализации](https://python-all.ru/3/c-api/extension-modules.html#extension-export-hook) (`PyInit_modulename`) должна создать, наполнить и вернуть объект модуля. Обычно это делается с помощью [`PyModule_Create()`](https://python-all.ru/3/c-api/module.html#c.PyModule_Create) и таких функций, как [`PyModule_AddObjectRef()`](https://python-all.ru/3/c-api/module.html#c.PyModule_AddObjectRef).113114Однофазная инициализация отличается от [стандартной](https://python-all.ru/3/c-api/extension-modules.html#multi-phase-initialization) следующим образом:115116- Однофазные модули являются, или, точнее, *содержат* «одиночки» (синглтоны).117118 При первой инициализации модуля Python сохраняет содержимое `__dict__` модуля (то есть, как правило, функции и типы модуля).119120 При последующих импортах Python не вызывает функцию инициализации снова. Вместо этого он создаёт новый объект модуля с новым `__dict__` и копирует в него сохранённое содержимое. Например, для однофазного модуля `_testsinglephase` [\[1\]](https://python-all.ru/3/c-api/extension-modules.html#testsinglephase), который определяет функцию `sum` и класс исключения `error`:121122 ```python123 >>> import sys124 >>> import _testsinglephase as one125 >>> del sys.modules['_testsinglephase']126 >>> import _testsinglephase as two127 >>> one is two128 False129 >>> one.__dict__ is two.__dict__130 False131 >>> one.sum is two.sum132 True133 >>> one.error is two.error134 True135 ```136137 Точное поведение следует считать деталью реализации CPython.138- Чтобы обойти тот факт, что `PyInit_modulename` не принимает аргумент *spec*, некоторое состояние механизма импорта сохраняется и применяется к первому подходящему модулю, созданному во время вызова `PyInit_modulename`. В частности, при импорте подмодуля этот механизм добавляет имя родительского пакета к имени модуля.139140 Функция `PyInit_modulename` однофазного модуля должна создать «свой» объект модуля как можно раньше, до того как могут быть созданы любые другие объекты модулей.141- Не-ASCII имена модулей (`PyInitU_modulename`) не поддерживаются.142- Однофазные модули поддерживают функции поиска модулей, такие как [`PyState_FindModule()`](https://python-all.ru/3/c-api/module.html#c.PyState_FindModule).143144\[[1](https://python-all.ru/3/c-api/extension-modules.html#id2)\]145146`_testsinglephase` – это внутренний модуль, используемый в наборе тестов CPython; ваша установка может включать его, а может и нет.147