Часто задаваемые вопросы о расширении и встраивании
- Можно ли создавать собственные функции на C?
- Можно ли создавать собственные функции на C++?
- Писать на C сложно. Есть ли альтернативы?
- Как выполнить произвольные инструкции Python из C?
- Как вычислить произвольное выражение Python из C?
- Как извлечь значения C из объекта Python?
- Как использовать Py_BuildValue() для создания кортежа произвольной длины?
- Как вызвать метод объекта из C?
- Как перехватить вывод PyErr_Print() (или любой другой вывод в stdout/stderr)?
- Как получить доступ из C к модулю, написанному на Python?
- Как взаимодействовать с объектами C++ из Python?
- Я добавил модуль с помощью файла Setup, но make завершается с ошибкой. Почему?
- Как отлаживать расширение?
- Я хочу скомпилировать модуль 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() предоставляет указатель на их значение и их длину. Обратите внимание: байтовые объекты Python могут содержать нулевые байты, поэтому не следует использовать strlen() из C.
Чтобы проверить тип объекта, сначала убедитесь, что это не 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, но make завершается с ошибкой. Почему?
Файл 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 Modules) и скомпоновать модуль расширения с помощью 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 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/faq/extending.html