Часто задаваемые вопросы о расширении и встраивании
- Можно ли создавать собственные функции на C?
- Можно ли создавать собственные функции на C++?
- Писать на C сложно; есть ли альтернативы?
- Как выполнять произвольные инструкции Python из C?
- Как вычислить произвольное выражение Python из C?
- Как извлечь значения C из объекта Python?
- Как использовать Py_BuildValue() для создания кортежа произвольной длины?
- Как вызвать метод объекта из C?
- Как перехватить вывод PyErr_Print() (или любой другой вывод в stdout/stderr)?
- Как получить доступ из C к модулю, написанному на Python?
- Как взаимодействовать с объектами C++ из Python?
- Я добавил модуль с помощью файла Setup, но сборка завершается с ошибкой. Почему?
- Как отлаживать расширение?
- Я хочу скомпилировать модуль Python в своей системе Linux, но некоторых файлов не хватает. Почему?
- Как отличить «неполный ввод» от «недопустимого ввода»?
- Как найти неопределённые символы g++ __builtin_new или __pure_virtual?
- Можно ли создать класс объектов, некоторые методы которого реализованы на C, а другие — на Python (например, с помощью наследования)?
Можно ли создавать собственные функции на C?
Да, можно создавать встроенные модули, содержащие функции, переменные, исключения и даже новые типы на C. Это описано в документе Расширение и встраивание интерпретатора Python.
Эта тема также рассматривается в большинстве учебников по Python среднего и продвинутого уровня.
Можно ли создавать собственные функции на C++?
Да, используя имеющиеся в C++ средства совместимости с C. Разместите extern "C" {
... } вокруг файлов включения Python и добавьте extern "C" перед каждой функцией, которую будет вызывать интерпретатор Python. Глобальные или статические объекты C++ с конструкторами, вероятно, использовать не стоит.
Писать на C сложно; есть ли альтернативы?
В зависимости от того, что именно вы хотите сделать, существует ряд альтернатив написанию собственных расширений на C. Рекомендуемые сторонние инструменты предлагают как более простые, так и более продвинутые способы создания расширений Python на C и C++.
Как выполнять произвольные инструкции Python из C?
Для этого предназначена функция самого высокого уровня PyRun_SimpleString(), которая принимает единственный строковый аргумент, выполняемый в контексте модуля __main__, и возвращает 0 в случае успеха и -1 при возникновении исключения (в том числе SyntaxError). Если требуется больший контроль, используйте PyRun_String(); см. исходный код PyRun_SimpleString() в Python/pythonrun.c.
Как вычислить произвольное выражение Python из C?
Вызовите функцию PyRun_String() из предыдущего вопроса, указав начальный символ Py_eval_input; она разбирает выражение, вычисляет его и возвращает результат.
Как извлечь значения C из объекта Python?
Это зависит от типа объекта. Если это кортеж, PyTuple_Size() возвращает его длину, а PyTuple_GetItem() возвращает элемент с заданным индексом. Для списков существуют аналогичные функции: PyList_Size() и PyList_GetItem().
Для байтовых строк PyBytes_Size() возвращает их длину, а PyBytes_AsStringAndSize() предоставляет указатель на их значение и их длину. Обратите внимание, что объекты bytes в Python могут содержать нулевые байты, поэтому не следует использовать C-функцию strlen().
Чтобы проверить тип объекта, сначала убедитесь, что он не равен NULL, а затем используйте PyBytes_Check(), PyTuple_Check(), PyList_Check() и т. д.
Для работы с объектами Python также существует высокоуровневый API, предоставляемый так называемым «абстрактным» интерфейсом. Подробнее см. Include/abstract.h. Он позволяет взаимодействовать с последовательностями Python любых типов с помощью вызовов вроде PySequence_Length(), PySequence_GetItem() и т. д., а также поддерживает множество других полезных протоколов, например числовые операции (PyNumber_Index() и другие) и отображения в API PyMapping.
Как использовать Py_BuildValue() для создания кортежа произвольной длины?
Никак. Вместо этого используйте PyTuple_Pack().
Как вызвать метод объекта из C?
Функцию PyObject_CallMethod() можно использовать для вызова произвольного метода объекта. Параметры: объект, имя вызываемого метода, строка формата, аналогичная используемой в Py_BuildValue(), и значения аргументов:
PyObject *
PyObject_CallMethod(PyObject *object, const char *method_name,
const char *arg_format, ...);
Это работает с любым объектом, имеющим методы, — как со встроенными, так и с пользовательскими. Не забудьте в итоге вызвать Py_DECREF() для возвращённого значения.
Например, чтобы вызвать метод «seek» файлового объекта с аргументами 10 и 0 (предполагая, что указатель на файловый объект — «f»):
res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
... an exception occurred ...
}
else {
Py_DECREF(res);
}
Обратите внимание: поскольку PyObject_CallObject() всегда требует кортеж в качестве списка аргументов, для вызова функции без аргументов укажите формат «()», а для вызова функции с одним аргументом заключите аргумент в скобки, например «(i)».
Как перехватить вывод PyErr_Print() (или любой другой вывод в stdout/stderr)?
В коде Python определите объект, поддерживающий метод write(). Присвойте этот объект переменным sys.stdout и sys.stderr. Вызовите print_error или просто позвольте стандартному механизму трассировки стека выполнить свою работу. Тогда вывод будет направлен туда, куда его отправляет ваш метод write().
Проще всего сделать это с помощью класса io.StringIO:
>>> import io, sys
>>> sys.stdout = io.StringIO()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(sys.stdout.getvalue())
foo
hello world!
Пользовательский объект для той же цели может выглядеть так:
>>> 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!
Как получить доступ из C к модулю, написанному на Python?
Указатель на объект модуля можно получить следующим образом:
module = PyImport_ImportModule("<modulename>");
Если модуль ещё не был импортирован (то есть его ещё нет в sys.modules), этот вызов инициализирует модуль; в противном случае он просто возвращает значение sys.modules["<modulename>"]. Обратите внимание, что модуль не добавляется ни в какое пространство имён — вызов лишь гарантирует, что модуль инициализирован и сохранён в sys.modules.
Затем можно получить доступ к атрибутам модуля (то есть к любым именам, определённым в модуле) следующим образом:
attr = PyObject_GetAttrString(module, "<attrname>");
Также можно присваивать значения переменным в модуле с помощью вызова PyObject_SetAttrString().
Как взаимодействовать с объектами C++ из Python?
В зависимости от ваших требований существует множество подходов. Чтобы сделать это вручную, начните с чтения документа «Расширение и встраивание». Следует учитывать, что для среды выполнения Python между C и C++ не так уж много различий, поэтому подход с созданием нового типа Python на основе типа структуры (указателя) C подойдёт и для объектов C++.
Сведения о библиотеках C++ см. в разделе Писать на C сложно; есть ли альтернативы?.
Я добавил модуль с помощью файла Setup, но сборка завершается с ошибкой. Почему?
Файл Setup должен заканчиваться символом новой строки. Если его нет, сборка завершается с ошибкой. (Исправление потребовало бы довольно сомнительных ухищрений со сценариями оболочки, а ошибка настолько незначительна, что усилия не кажутся оправданными.)
Как отлаживать расширение?
При использовании GDB с динамически загружаемыми расширениями нельзя установить точку останова в расширении, пока оно не загружено.
Добавьте команду в файл .gdbinit (или введите её в интерактивном режиме):
br _PyImport_LoadDynamicModule
Затем запустите GDB:
$ 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, но некоторых файлов не хватает. Почему?
В большинстве пакетных версий Python отсутствуют некоторые файлы, необходимые для компиляции расширений Python.
В Red Hat установите RPM-пакет python3-devel, чтобы получить необходимые файлы.
В Debian выполните apt-get install python3-dev.
Как отличить «неполный ввод» от «недопустимого ввода»?
Иногда требуется имитировать поведение интерактивного интерпретатора Python: он выводит приглашение продолжения ввода, если ввод не завершён (например, вы начали вводить инструкцию «if», но не закрыли скобки или тройные кавычки), и сразу же сообщает об ошибке синтаксиса, если ввод недопустим.
В Python можно использовать модуль codeop, который достаточно точно имитирует поведение парсера. Например, его использует IDLE.
Проще всего сделать это на C — вызвать PyRun_InteractiveLoop() (возможно, в отдельном потоке) и предоставить интерпретатору Python обработку ввода. Также можно установить PyOS_ReadlineFunctionPointer(), чтобы он указывал на вашу пользовательскую функцию ввода. Дополнительные рекомендации см. в Modules/readline.c и Parser/myreadline.c.
Как найти неопределённые символы g++ __builtin_new или __pure_virtual?
Чтобы динамически загружать модули расширений g++, необходимо перекомпилировать Python, повторно скомпоновать его с помощью g++ (изменив LINKCC в файле Makefile для модулей Python) и скомпоновать модуль расширения с помощью g++ (например, g++ -shared -o mymodule.so mymodule.o).
Можно ли создать класс объектов, некоторые методы которого реализованы на C, а другие — на Python (например, с помощью наследования)?
Да, можно наследовать встроенные классы, такие как int, list, dict и т. д.
Библиотека Boost Python (BPL, https://www.boost.org/libs/python/doc/index.html) позволяет делать это на C++ (то есть с помощью BPL можно наследовать класс расширения, написанный на C++).
© 2001–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/faq/extending.html