Часто задаваемые вопросы по расширению/встраиванию
- Можно ли создать свои собственные функции на C?
- Можно ли создать свои собственные функции на C++?
- Написание на C сложно; есть ли альтернативы?
- Как выполнить произвольные утверждения Python из C?
- Как оценить произвольное выражение Python из C?
- Как извлечь значения C из объекта Python?
- Как использовать Py_BuildValue() для создания кортежа произвольной длины?
- Как вызвать метод объекта из C?
- Как перехватить вывод из PyErr_Print() (или любого вывода в stdout/stderr)?
- Как получить доступ к модулю, написанному на Python, из C?
- Как взаимодействовать с объектами 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, в зависимости от того, что вы пытаетесь сделать.
Cython и его аналог Pyrex — это компиляторы, которые принимают слегка изменённую форму Python и генерируют соответствующий код C. Cython и Pyrex позволяют написать расширение, не изучая C API Python.
Если вам нужно взаимодействовать с какой-либо библиотекой C или C++, для которой в настоящее время нет расширения Python, вы можете попробовать обернуть типы данных и функции библиотеки с помощью инструмента, такого как SWIG. SIP, CXX Boost или Weave также являются альтернативами для обертывания библиотек 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 байтов могут содержать нулевые байты, поэтому C’s strlen() не следует использовать.
Чтобы проверить тип объекта, сначала убедитесь, что он не NULL, а затем используйте PyBytes_Check(), PyTuple_Check(), PyList_Check() и т. д.
Также существует API высокого уровня для объектов Python, который предоставляется так называемым «абстрактным» интерфейсом — прочитайте 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!
Как получить доступ к модулю, написанному на Python, из C?
Вы можете получить указатель на объект модуля следующим образом:
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 не включают каталог /usr/lib/python2.x/config/, который содержит различные файлы, необходимые для компиляции расширений Python.
Для Red Hat установите пакет python-devel RPM, чтобы получить необходимые файлы.
Для Debian выполните apt-get install python-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++ (т.е. вы можете наследовать от класса расширения, написанного на C++, с использованием BPL).
© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/faq/extending.html