Spec-Zone.ru › Python 3.14

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

  • Можно ли создавать собственные функции на 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

Spec-Zone.ru

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