> **Источник:** https://python-all.ru/3.13/faq/extending.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# [FAQ по расширению и встраиванию](https://python-all.ru/3.13/faq/extending.html#id2)

Содержание [FAQ по расширению и встраиванию](https://python-all.ru/3.13/faq/extending.html#extending-embedding-faq) [Можно ли создавать собственные функции на C?](https://python-all.ru/3.13/faq/extending.html#can-i-create-my-own-functions-in-c) [Можно ли создавать собственные функции на C++?](https://python-all.ru/3.13/faq/extending.html#id1) [Писать на C сложно; есть ли альтернативы?](https://python-all.ru/3.13/faq/extending.html#writing-c-is-hard-are-there-any-alternatives) [Как выполнить произвольные инструкции Python из C?](https://python-all.ru/3.13/faq/extending.html#how-can-i-execute-arbitrary-python-statements-from-c) [Как вычислить произвольное выражение Python из C?](https://python-all.ru/3.13/faq/extending.html#how-can-i-evaluate-an-arbitrary-python-expression-from-c) [Как извлечь значения C из объекта Python?](https://python-all.ru/3.13/faq/extending.html#how-do-i-extract-c-values-from-a-python-object) [Как использовать Py\_BuildValue() для создания кортежа произвольной длины?](https://python-all.ru/3.13/faq/extending.html#how-do-i-use-py-buildvalue-to-create-a-tuple-of-arbitrary-length) [Как вызвать метод объекта из C?](https://python-all.ru/3.13/faq/extending.html#how-do-i-call-an-object-s-method-from-c) [Как перехватить вывод PyErr\_Print() (или любого другого вывода в stdout/stderr)?](https://python-all.ru/3.13/faq/extending.html#how-do-i-catch-the-output-from-pyerr-print-or-anything-that-prints-to-stdout-stderr) [Как получить доступ к модулю, написанному на Python, из C?](https://python-all.ru/3.13/faq/extending.html#how-do-i-access-a-module-written-in-python-from-c) [Как организовать взаимодействие с объектами C++ из Python?](https://python-all.ru/3.13/faq/extending.html#how-do-i-interface-to-c-objects-from-python) [Почему make завершается ошибкой после добавления модуля через файл Setup?](https://python-all.ru/3.13/faq/extending.html#i-added-a-module-using-the-setup-file-and-the-make-fails-why) [Как отлаживать расширение?](https://python-all.ru/3.13/faq/extending.html#how-do-i-debug-an-extension) [Почему при компиляции модуля Python в Linux отсутствуют некоторые файлы?](https://python-all.ru/3.13/faq/extending.html#i-want-to-compile-a-python-module-on-my-linux-system-but-some-files-are-missing-why) [Как отличить «неполный ввод» от «неверного ввода»?](https://python-all.ru/3.13/faq/extending.html#how-do-i-tell-incomplete-input-from-invalid-input) [Как найти неопределённые символы g++ \_\_builtin\_new или \_\_pure\_virtual?](https://python-all.ru/3.13/faq/extending.html#how-do-i-find-undefined-g-symbols-builtin-new-or-pure-virtual) [Можно ли создать класс объекта, в котором часть методов реализована на C, а часть – на Python (например, через наследование)?](https://python-all.ru/3.13/faq/extending.html#can-i-create-an-object-class-with-some-methods-implemented-in-c-and-others-in-python-e-g-through-inheritance)

## [Могу ли я создавать собственные функции на C?](https://python-all.ru/3.13/faq/extending.html#id3)

Да, можно создавать встроенные модули, содержащие функции, переменные, исключения и даже новые типы на C. Это описано в документе [Extending and Embedding the Python Interpreter](https://python-all.ru/3.13/extending/index.html#extending-index).

Эта тема также освещается в большинстве книг по Python для среднего и продвинутого уровня.

## [Могу ли я создавать собственные функции на C++?](https://python-all.ru/3.13/faq/extending.html#id4)

Да, это возможно благодаря средствам совместимости с C в C++. Следует поместить `extern "C" { ... }` вокруг включаемых файлов Python и добавить `extern "C"` перед каждой функцией, которая будет вызываться интерпретатором Python. Глобальные или статические объекты C++ с конструкторами, вероятно, лучше не использовать.

## [Писать на C сложно; есть ли альтернативы?](https://python-all.ru/3.13/faq/extending.html#id5)

Существует несколько альтернатив написанию собственных расширений на C, в зависимости от поставленной задачи. [Рекомендуемые сторонние инструменты](https://python-all.ru/3.13/c-api/intro.html#c-api-tools) предлагают как более простые, так и более сложные подходы к созданию расширений на C и C++ для Python.

## [Как выполнить произвольные операторы Python из C?](https://python-all.ru/3.13/faq/extending.html#id6)

Функция самого высокого уровня для этого – [`PyRun_SimpleString()`](https://python-all.ru/3.13/c-api/veryhigh.html#c.PyRun_SimpleString), принимающая один строковый аргумент, который выполняется в контексте модуля `__main__`, и возвращающая `0` в случае успеха и `-1` при возникновении исключения (включая [`SyntaxError`](https://python-all.ru/3.13/library/exceptions.html#SyntaxError)). Если требуется более тонкий контроль, используйте [`PyRun_String()`](https://python-all.ru/3.13/c-api/veryhigh.html#c.PyRun_String); см. исходный код `PyRun_SimpleString()` в `Python/pythonrun.c`.

## [Как вычислить произвольное выражение Python из C?](https://python-all.ru/3.13/faq/extending.html#id7)

Вызовите функцию [`PyRun_String()`](https://python-all.ru/3.13/c-api/veryhigh.html#c.PyRun_String) из предыдущего вопроса с начальным символом [`Py_eval_input`](https://python-all.ru/3.13/c-api/veryhigh.html#c.Py_eval_input); она разбирает выражение, вычисляет его и возвращает его значение.

## [Как извлечь значения C из объекта Python?](https://python-all.ru/3.13/faq/extending.html#id8)

Это зависит от типа объекта. Если это кортеж, [`PyTuple_Size()`](https://python-all.ru/3.13/c-api/tuple.html#c.PyTuple_Size) возвращает его длину, а [`PyTuple_GetItem()`](https://python-all.ru/3.13/c-api/tuple.html#c.PyTuple_GetItem) возвращает элемент по указанному индексу. У списков есть аналогичные функции: [`PyList_Size()`](https://python-all.ru/3.13/c-api/list.html#c.PyList_Size) и [`PyList_GetItem()`](https://python-all.ru/3.13/c-api/list.html#c.PyList_GetItem).

Для bytes [`PyBytes_Size()`](https://python-all.ru/3.13/c-api/bytes.html#c.PyBytes_Size) возвращает его длину, а [`PyBytes_AsStringAndSize()`](https://python-all.ru/3.13/c-api/bytes.html#c.PyBytes_AsStringAndSize) предоставляет указатель на его значение и его длину. Обратите внимание, что объекты Python bytes могут содержать нулевые байты, поэтому C'шный `strlen()` использовать не следует.

Чтобы проверить тип объекта, сначала убедитесь, что он не `NULL`, а затем используйте [`PyBytes_Check()`](https://python-all.ru/3.13/c-api/bytes.html#c.PyBytes_Check), [`PyTuple_Check()`](https://python-all.ru/3.13/c-api/tuple.html#c.PyTuple_Check), [`PyList_Check()`](https://python-all.ru/3.13/c-api/list.html#c.PyList_Check) и т.д.

Существует также высокоуровневый API для объектов Python, предоставляемый так называемым «абстрактным» интерфейсом – читайте `Include/abstract.h` для получения дополнительных сведений. Он позволяет взаимодействовать с любыми последовательностями Python с помощью вызовов вроде [`PySequence_Length()`](https://python-all.ru/3.13/c-api/sequence.html#c.PySequence_Length), [`PySequence_GetItem()`](https://python-all.ru/3.13/c-api/sequence.html#c.PySequence_GetItem) и т.д., а также многих других полезных протоколов, таких как числа ([`PyNumber_Index()`](https://python-all.ru/3.13/c-api/number.html#c.PyNumber_Index) и др.) и отображения в API PyMapping.

## [Как использовать Py\_BuildValue() для создания кортежа произвольной длины?](https://python-all.ru/3.13/faq/extending.html#id9)

Никак. Вместо этого используйте [`PyTuple_Pack()`](https://python-all.ru/3.13/c-api/tuple.html#c.PyTuple_Pack).

## [Как вызвать метод объекта из C?](https://python-all.ru/3.13/faq/extending.html#id10)

Функцию [`PyObject_CallMethod()`](https://python-all.ru/3.13/c-api/call.html#c.PyObject_CallMethod) можно использовать для вызова произвольного метода объекта. Параметры: объект, имя вызываемого метода, строка форматирования, подобная используемой с [`Py_BuildValue()`](https://python-all.ru/3.13/c-api/arg.html#c.Py_BuildValue), и значения аргументов:

```c
PyObject *
PyObject_CallMethod(PyObject *object, const char *method_name,
                    const char *arg_format, ...);
```

Это работает для любого объекта, имеющего методы – как встроенные, так и определённые пользователем. Ответственность за последующий [`Py_DECREF()`](https://python-all.ru/3.13/c-api/refcounting.html#c.Py_DECREF) возвращаемого значения лежит на вас.

Например, чтобы вызвать метод “seek” файлового объекта с аргументами 10, 0 (предполагая, что указатель на файловый объект – “f”):

```c
res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
        ... an exception occurred ...
}
else {
        Py_DECREF(res);
}
```

Обратите внимание, что [`PyObject_CallObject()`](https://python-all.ru/3.13/c-api/call.html#c.PyObject_CallObject) *всегда* ожидает кортеж в качестве списка аргументов; чтобы вызвать функцию без аргументов, передайте “()” в качестве формата, а чтобы вызвать функцию с одним аргументом, заключите аргумент в круглые скобки, например “(i)”.

## [Как перехватить вывод PyErr\_Print() (или всего, что печатает в stdout/stderr)?](https://python-all.ru/3.13/faq/extending.html#id11)

В коде Python определите объект, поддерживающий метод `write()`. Назначьте этот объект на [`sys.stdout`](https://python-all.ru/3.13/library/sys.html#sys.stdout) и [`sys.stderr`](https://python-all.ru/3.13/library/sys.html#sys.stderr). Вызовите print\_error или просто позвольте стандартному механизму трассировки сработать. Тогда вывод будет направлен туда, куда его отправляет метод `write()`.

Проще всего это сделать с помощью класса [`io.StringIO`](https://python-all.ru/3.13/library/io.html#io.StringIO):

```pycon
>>> import io, sys
>>> sys.stdout = io.StringIO()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(sys.stdout.getvalue())
foo
hello world!
```

Пользовательский объект для того же самого мог бы выглядеть так:

```pycon
>>> import io, sys
>>> class StdoutCatcher(io.TextIOBase):
...     def __init__(self):
...         self.data = []
...     def write(self, stuff):
...         self.data.append(stuff)
...
>>> import sys
>>> sys.stdout = StdoutCatcher()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(''.join(sys.stdout.data))
foo
hello world!
```

## [Как получить доступ к модулю, написанному на Python, из C?](https://python-all.ru/3.13/faq/extending.html#id12)

Указатель на объект модуля можно получить следующим образом:

```c
module = PyImport_ImportModule("<modulename>");
```

Если модуль ещё не был импортирован (т.е. его ещё нет в [`sys.modules`](https://python-all.ru/3.13/library/sys.html#sys.modules)), то выполняется его инициализация; в противном случае просто возвращается значение `sys.modules["<modulename>"]`. Обратите внимание: модуль не добавляется ни в одно пространство имён – гарантируется только его инициализация и сохранение в `sys.modules`.

Затем можно обратиться к атрибутам модуля (т.е. к любым именам, определённым в модуле) следующим образом:

```c
attr = PyObject_GetAttrString(module, "<attrname>");
```

Вызов [`PyObject_SetAttrString()`](https://python-all.ru/3.13/c-api/object.html#c.PyObject_SetAttrString) для присваивания переменным в модуле также работает.

## [Как организовать взаимодействие с объектами C++ из Python?](https://python-all.ru/3.13/faq/extending.html#id13)

В зависимости от требований существует множество подходов. Для ручного выполнения начните с чтения [документа «Extending and Embedding»](https://python-all.ru/3.13/extending/index.html#extending-index). Следует понимать, что для системы времени выполнения Python нет большой разницы между C и C++ – поэтому стратегия создания нового типа Python на основе C-структуры (указателя) будет работать и для объектов C++.

Для библиотек C++ см. [Писать на C сложно; есть ли альтернативы?](https://python-all.ru/3.13/faq/extending.html#c-wrapper-software).

## [Я добавил модуль через файл Setup, но make завершается ошибкой; почему?](https://python-all.ru/3.13/faq/extending.html#id14)

Файл Setup должен заканчиваться символом новой строки; если его нет, сборка завершается ошибкой. (Исправление требует уродливых ухищрений в shell-скриптах, и этот баг настолько незначителен, что не стоит затраченных усилий.)

## [Как отлаживать расширение?](https://python-all.ru/3.13/faq/extending.html#id15)

При использовании GDB с динамически загружаемыми расширениями невозможно установить точку останова в расширении до его загрузки.

В файле `.gdbinit` (или интерактивно) добавьте команду:

```text
br _PyImport_LoadDynamicModule
```

Затем при запуске GDB:

```console
$ gdb /local/bin/python
gdb) run myscript.py
gdb) continue # repeat until your extension is loaded
gdb) finish   # so that your extension is loaded
gdb) br myfunction.c:50
gdb) continue
```

## [Хочу скомпилировать модуль Python в своей системе Linux, но не хватает некоторых файлов. Почему?](https://python-all.ru/3.13/faq/extending.html#id16)

Большинство пакетных версий Python не включают некоторые файлы, необходимые для компиляции расширений Python.

Для Red Hat установите пакет python3-devel RPM, чтобы получить необходимые файлы.

Для Debian выполните `apt-get install python3-dev`.

## [Как отличить «неполный ввод» от «недопустимого ввода»?](https://python-all.ru/3.13/faq/extending.html#id17)

Иногда требуется эмулировать поведение интерактивного интерпретатора Python, где выдаётся приглашение продолжения ввода, когда ввод неполон (например, напечатано начало инструкции «if» или не закрыты скобки или тройные кавычки), но при недопустимом вводе сразу выдаётся сообщение об ошибке синтаксиса.

В Python можно использовать модуль [`codeop`](https://python-all.ru/3.13/library/codeop.html#module-codeop), который достаточно точно воспроизводит поведение парсера. Например, так делает IDLE.

Самый простой способ сделать это в C – вызвать [`PyRun_InteractiveLoop()`](https://python-all.ru/3.13/c-api/veryhigh.html#c.PyRun_InteractiveLoop) (возможно, в отдельном потоке) и позволить интерпретатору Python обработать ввод за вас. Также можно установить [`PyOS_ReadlineFunctionPointer()`](https://python-all.ru/3.13/c-api/veryhigh.html#c.PyOS_ReadlineFunctionPointer) так, чтобы он указывал на вашу пользовательскую функцию ввода. Дополнительные подсказки см. в `Modules/readline.c` и `Parser/myreadline.c`.

## [Как найти неопределённые символы g++ \_\_builtin\_new или \_\_pure\_virtual?](https://python-all.ru/3.13/faq/extending.html#id18)

Для динамической загрузки модулей расширения g++ необходимо перекомпилировать Python, перекомпоновать его с помощью g++ (измените LINKCC в Makefile модулей Python) и скомпоновать модуль расширения с помощью g++ (например, `g++ -shared -o mymodule.so mymodule.o`).

## [Можно ли создать класс объектов, в котором одни методы реализованы на C, а другие – на Python (например, через наследование)?](https://python-all.ru/3.13/faq/extending.html#id19)

Да, можно наследовать от встроенных классов, таких как [`int`](https://python-all.ru/3.13/library/functions.html#int), [`list`](https://python-all.ru/3.13/library/stdtypes.html#list), [`dict`](https://python-all.ru/3.13/library/stdtypes.html#dict) и т.д.

Библиотека Boost Python (BPL, [https://www.boost.org/libs/python/doc/index.html](https://python-all.ru/3.13/faq/extending.html)) предоставляет способ сделать это из C++ (т.е. можно наследовать от класса расширения, написанного на C++, с помощью BPL).
