Spec-Zone.ru › Python 3.9

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

  • Могу ли я создать свои собственные функции на 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() возвращает элемент по указанному индексу. У списков похожие функции, 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 установите RPM пакет python-devel, чтобы получить необходимые файлы.

Для 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, http://www.boost.org/libs/python/doc/index.html) предоставляет способ сделать это из C++ (т. е. вы можете наследоваться от класса расширения, написанного на C++, с помощью BPL).

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

Spec-Zone.ru

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