Spec-Zone.ru › Python 3.13

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

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

Spec-Zone.ru

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