Spec-Zone.ru › Python 3.11

Часто задаваемые вопросы по расширению/встраиванию

  • Могу ли я создать собственные функции на 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 позволяют написать расширение, не изучая API C 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 типа bytes могут содержать нулевые байты, поэтому C должен использовать 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–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/faq/extending.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API