Часто задаваемые вопросы по расширению/пополнению¶
Содержание
Часто задаваемые вопросы по расширению/пополнению
Как я могу выполнять произвольные операторы Python из языка C?
Как использовать Py_BuildValue() для создания кортежа произвольной длины?
Как перехватить вывод PyErr_Print() (или что-либо, что печатает в stdout/stderr)?
Как получить доступ к модулю, написанному на Python, из языка C?
Я добавил модуль, используя файл Setup, и make не работает; почему?
Я хочу скомпилировать модуль Python в моей системе Linux, но некоторые файлы отсутствуют. Почему?
Как найти неопределенные символы g++ __builtin_new или __pure_virtual?
Могу ли я создавать свои собственные функции на языке C?¶
Да, вы можете создавать встроенные модули, содержащие функции, переменные, исключения и даже новые типы в C. Это объясняется в документе Расширение и встраивание интерпретатора Python.
В большинстве книг по Python для среднего и продвинутого уровня эта тема также будет рассмотрена.
Могу ли я создавать свои собственные функции в C++?¶
Да, используя возможности совместимости с C, которые есть в C++. Поместите extern "C" { ... }
вокруг включаемых файлов Python и поставьте extern "C"
перед каждой функцией, которая будет вызываться интерпретатором Python. Глобальные или статические объекты C++ с конструкторами, вероятно, не очень хорошая идея.
Писать на C сложно; есть ли альтернативы?¶
Существует несколько альтернатив написанию собственных расширений на C, в зависимости от того, что вы пытаетесь сделать.
Cython и родственный ему Pyrex - это компиляторы, которые принимают слегка измененную форму Python и генерируют соответствующий C-код. Cython и Pyrex позволяют написать расширение без необходимости изучать C API 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 могут содержать нулевые байты, поэтому не следует использовать 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()
„ing возвращаемое значение.
Чтобы вызвать, например, метод «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?¶
В зависимости от ваших требований, существует множество подходов. Чтобы сделать это вручную, начните с чтения the «Extending and Embedding» document. Поймите, что для системы времени выполнения Python нет большой разницы между C и C++ - поэтому стратегия создания нового типа Python на основе типа структуры (указателя) C будет работать и для объектов C++.
О библиотеках C++ смотрите Писать на C сложно; есть ли альтернативы?.
Я добавил модуль, используя файл Setup, и make не работает; почему?¶
Установка должна заканчиваться новой строкой, если новой строки нет, процесс сборки завершится неудачно. (Исправление этого требует некоторого уродливого взлома сценария оболочки, а эта ошибка настолько незначительна, что не стоит усилий).
Как отладить расширение?¶
При использовании 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 Library (BPL, https://www.boost.org/libs/python/doc/index.html) предоставляет возможность сделать это из C++ (т.е. вы можете наследовать от класса расширения, написанного на C++, используя BPL).