Spec-Zone.ru › Python 3.10

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

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

Если вам нужно взаимодействовать с какой-либо библиотекой 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() возвращает элемент по указанному индексу. У списков есть похожие функции, PyListSize() и PyList_GetItem().

Для байтов PyBytes_Size() возвращает длину, а PyBytes_AsStringAndSize() предоставляет указатель на значение и его длину. Обратите внимание, что объекты Python bytes могут содержать нулевые байты, поэтому 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 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++ (т.е. вы можете унаследовать от класса расширения, написанного на C++, с помощью BPL).

© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/faq/extending.html

Spec-Zone.ru

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