FAQ по удлинению/присоединению¶
Могу ли я создавать собственные функции на языке C?¶
Да, в Си можно создавать встроенные модули, содержащие функции, переменные, исключения и даже новые типы. Об этом рассказывается в документе Расширение и встраивание интерпретатора Python.
В большинстве книг по Python для среднего и продвинутого уровня эта тема также рассматривается.
Можно ли создавать собственные функции в C++?¶
Да, используя возможности совместимости с C, которые есть в C++. Поместите extern "C" { ... }
вокруг включаемых файлов Python и поставьте extern "C"
перед каждой функцией, которая будет вызываться интерпретатором Python. Глобальные или статические объекты C++ с конструкторами, вероятно, не самая лучшая идея.
Писать на C сложно; есть ли альтернативы?¶
Существует несколько альтернатив написанию собственных расширений на C, в зависимости от того, что вы хотите сделать.
Cython и его родственник Pyrex - это компиляторы, которые принимают слегка измененную форму языка Python и генерируют соответствующий код на языке C. Cython и Pyrex позволяют написать расширение без необходимости изучать C API Python.
If you need to interface to some C or C++ library for which no Python extension currently exists, you can try wrapping the library’s data types and functions with a tool such as SWIG. SIP, CXX Boost, or Weave are also alternatives for wrapping C++ libraries.
Как выполнить произвольные операторы 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()
и т. д.
Существует также высокоуровневый 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 установите 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) и перелинковать ваш модуль расширения с помощью 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).