Extending/Embedding FAQ¶
内容
- 扩展/嵌入常见问题
- 我可以在C中创建自己的函数吗
- 我可以在C ++中创建自己的函数吗
- 写C很难;有什么替代方案吗?
- 如何从C?执行任意Python语句
- 如何从C?计算任意的Python表达式
- 如何从Python对象提取C值?
- 如何使用Py_BuildValue()创建任意长度的元组?
- 如何从C?调用对象的方法
- 如何捕获来自PyErr_Print()(或打印到stdout / stderr的任何内容)的输出?
- 如何使用C?访问以Python编写的模块
- 如何从Python连接到C ++对象?
- 我使用安装文件添加了一个模块,make失败;为什么?
- 如何调试扩展程序?
- 我想在我的Linux系统上编译一个Python模块,但有些文件丢失。为什么?
- 如何从“无效输入”中识别“不完整输入”?
- 如何查找未定义的g ++符号__builtin_new或__pure_virtual?
- 我可以创建一个对象类,使用C中实现的一些方法和Python中的其他方法(例如通过继承)?
Can I create my own functions in C?¶
是的,您可以在C中创建包含函数,变量,异常甚至是新类型的内建模块。这在文档Extending and Embedding the Python Interpreter中进行了解释。
大多数中级或高级Python书籍也将涵盖这个主题。
Can I create my own functions in C++?¶
是的,使用C的C兼容性功能。放置extern “C” { ... } t0>周围的Python包含文件,并在每个将被Python解释器调用的函数之前放置
使用构造函数的全局或静态C ++对象可能不是一个好主意。extern “C”
Writing C is hard; are there any alternatives?¶
有许多替代方法来编写自己的C扩展,这取决于你想要做什么。
Cython及其相对的Pyrex是接受稍微修改的Python形式并生成相应C代码的编译器。Cython和Pyrex使编写扩展成为可能,而不必学习Python的C API。
如果需要连接到一些没有Python扩展的C或C ++库,可以尝试使用诸如SWIG等工具包装库的数据类型和函数。SIP,CXX Boost或Weave也是用于包装C ++库的替代方法。
How can I execute arbitrary Python statements from C?¶
执行此操作的最高级函数是PyRun_SimpleString()
,它接受要在模块__main__
的上下文中执行的单个字符串参数,并返回0表示成功,-1当发生异常(包括SyntaxError
)时。如果你想要更多的控制,使用PyRun_String()
;请参阅Python/pythonrun.c
中PyRun_SimpleString()
的源代码。
How can I evaluate an arbitrary Python expression from C?¶
使用开始符号Py_eval_input
调用上一个问题的函数PyRun_String()
;它解析一个表达式,计算它并返回它的值。
How do I extract C values from a Python object?¶
这取决于对象的类型。如果它是一个元组,PyTuple_Size()
返回其长度,PyTuple_GetItem()
返回指定索引的项目。列表具有类似的函数,PyListSize()
和PyList_GetItem()
。
对于字节,PyBytes_Size()
返回其长度,PyBytes_AsStringAndSize()
提供一个指向其值及其长度的指针。请注意,Python字节对象可能包含空字节,因此不应使用C的strlen()
。
To test the type of an object, first make sure it isn’t NULL, and then use PyBytes_Check()
, PyTuple_Check()
, PyList_Check()
, etc.
还有一个高级API到Python对象,由所谓的“抽象”接口提供 - 有关更多详细信息,请参阅Include/abstract.h
。它允许使用PySequence_Length()
,PySequence_GetItem()
等调用与任何类型的Python序列交互。以及许多其他有用的协议,例如数字(PyNumber_Index()
等)和PyMapping API中的映射。
How do I call an object’s method from C?¶
PyObject_CallMethod()
函数可用于调用对象的任意方法。参数是对象,要调用的方法的名称,与Py_BuildValue()
一起使用的格式字符串,以及参数值:
PyObject *
PyObject_CallMethod(PyObject *object, const char *method_name,
const char *arg_format, ...);
这适用于具有方法的任何对象 - 无论是内建还是用户定义的。您最终对Py_DECREF()
负责返回值负责。
使用参数10,0(假设文件对象指针为“f”)调用文件对象的“seek”方法:
res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
... an exception occurred ...
}
else {
Py_DECREF(res);
}
请注意,因为PyObject_CallObject()
总是需要一个元组作为参数列表,调用一个没有参数的函数,传递“()”作为格式,带有一个参数,用括号括住参数,例如“(一世)”。
How do I catch the output from PyErr_Print() (or anything that prints to 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!
How do I access a module written in Python from C?¶
您可以获得指向模块对象的指针,如下所示:
module = PyImport_ImportModule("<modulename>");
如果模块尚未导入(即它尚未出现在sys.modules
)中,这将初始化模块;否则只是返回sys.modules["<modulename>"]
的值。请注意,它不会将模块输入任何命名空间 - 它仅确保它已被初始化并存储在sys.modules
中。
然后,您可以访问模块的属性。在模块中定义的任何名称)如下:
attr = PyObject_GetAttrString(module, "<attrname>");
调用PyObject_SetAttrString()
以分配给模块中的变量也可以工作。
How do I interface to C++ objects from Python?¶
根据您的要求,有很多方法。要手动执行此操作,请先阅读the “Extending and Embedding” document。意识到对于Python运行时系统,C和C ++之间没有太大的区别 - 因此围绕C结构(指针)类型构建一个新的Python类型的策略也适用于C ++对象。
对于C ++库,请参阅Writing C is hard; are there any alternatives?。
I added a module using the Setup file and the make fails; why?¶
安装程序必须以换行符结束,如果没有换行符,则构建过程失败。(修复这需要一些丑陋的shell脚本hackery,这个bug是如此轻微,它似乎不值得的努力。)
How do I debug an extension?¶
当使用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
I want to compile a Python module on my Linux system, but some files are missing. Why?¶
大多数打包版本的Python不包括/ usr / lib / python2. x / config /
目录,其中包含编译Python扩展所需的各种文件。
对于Red Hat,安装python-devel RPM以获取必要的文件。
对于Debian,请运行apt-get 安装 python-dev
。
How do I tell “incomplete input” from “invalid input”?¶
有时你想模仿Python交互式解释器的行为,当输入不完整时,它会给你一个继续提示。你键入了“if”语句的开头,或者没有关闭括号或三字符串引号),但是当输入无效时,它立即给出语法错误消息。
在Python中,您可以使用codeop
模块,它可以充分近似解析器的行为。IDLE使用这个,例如。
在C中最简单的方法是调用PyRun_InteractiveLoop()
(可能在一个单独的线程中),让Python解释器处理你的输入。您还可以将PyOS_ReadlineFunctionPointer()
设置为指向自定义输入函数。有关更多提示,请参见Modules/readline.c
和Parser/myreadline.c
。
但是有时你必须在与其他应用程序相同的线程中运行嵌入式Python解释器,并且不能允许PyRun_InteractiveLoop()
在等待用户输入时停止。一个解决方案是调用PyParser_ParseString()
,并测试e.error
等于E_EOF
,这意味着输入不完整)。这里有一个示例代码片段,未经测试,灵感来自Alex Farber的代码:
#include <Python.h>
#include <node.h>
#include <errcode.h>
#include <grammar.h>
#include <parsetok.h>
#include <compile.h>
int testcomplete(char *code)
/* code should end in \n */
/* return -1 for error, 0 for incomplete, 1 for complete */
{
node *n;
perrdetail e;
n = PyParser_ParseString(code, &_PyParser_Grammar,
Py_file_input, &e);
if (n == NULL) {
if (e.error == E_EOF)
return 0;
return -1;
}
PyNode_Free(n);
return 1;
}
另一个解决方案是尝试使用Py_CompileString()
编译接收的字符串。如果编译没有错误,尝试通过调用PyEval_EvalCode()
来执行返回的代码对象。否则保存输入以供以后使用。如果编译失败,请通过从异常元组中提取消息字符串并将其与字符串“解析时意外的EOF”进行比较,找出是错误还是只需要更多输入。这里是一个使用GNU readline库的完整示例(在调用readline()时可能需要忽略SIGINT):
#include <stdio.h>
#include <readline.h>
#include <Python.h>
#include <object.h>
#include <compile.h>
#include <eval.h>
int main (int argc, char* argv[])
{
int i, j, done = 0; /* lengths of line, code */
char ps1[] = ">>> ";
char ps2[] = "... ";
char *prompt = ps1;
char *msg, *line, *code = NULL;
PyObject *src, *glb, *loc;
PyObject *exc, *val, *trb, *obj, *dum;
Py_Initialize ();
loc = PyDict_New ();
glb = PyDict_New ();
PyDict_SetItemString (glb, "__builtins__", PyEval_GetBuiltins ());
while (!done)
{
line = readline (prompt);
if (NULL == line) /* Ctrl-D pressed */
{
done = 1;
}
else
{
i = strlen (line);
if (i > 0)
add_history (line); /* save non-empty lines */
if (NULL == code) /* nothing in code yet */
j = 0;
else
j = strlen (code);
code = realloc (code, i + j + 2);
if (NULL == code) /* out of memory */
exit (1);
if (0 == j) /* code was empty, so */
code[0] = '\0'; /* keep strncat happy */
strncat (code, line, i); /* append line to code */
code[i + j] = '\n'; /* append '\n' to code */
code[i + j + 1] = '\0';
src = Py_CompileString (code, "<stdin>", Py_single_input);
if (NULL != src) /* compiled just fine - */
{
if (ps1 == prompt || /* ">>> " or */
'\n' == code[i + j - 1]) /* "... " and double '\n' */
{ /* so execute it */
dum = PyEval_EvalCode (src, glb, loc);
Py_XDECREF (dum);
Py_XDECREF (src);
free (code);
code = NULL;
if (PyErr_Occurred ())
PyErr_Print ();
prompt = ps1;
}
} /* syntax error or E_EOF? */
else if (PyErr_ExceptionMatches (PyExc_SyntaxError))
{
PyErr_Fetch (&exc, &val, &trb); /* clears exception! */
if (PyArg_ParseTuple (val, "sO", &msg, &obj) &&
!strcmp (msg, "unexpected EOF while parsing")) /* E_EOF */
{
Py_XDECREF (exc);
Py_XDECREF (val);
Py_XDECREF (trb);
prompt = ps2;
}
else /* some other syntax error */
{
PyErr_Restore (exc, val, trb);
PyErr_Print ();
free (code);
code = NULL;
prompt = ps1;
}
}
else /* some non-syntax error */
{
PyErr_Print ();
free (code);
code = NULL;
prompt = ps1;
}
free (line);
}
}
Py_XDECREF(glb);
Py_XDECREF(loc);
Py_Finalize();
exit(0);
}
How do I find undefined g++ symbols __builtin_new or __pure_virtual?¶
要动态加载g ++扩展模块,必须重新编译Python,使用g ++重新链接它(在Python模块Makefile中更改LINKCC),并使用g ++链接扩展模块(例如,g ++ -shared -o mymodule.so mymodule.o
)。
Can I create an object class with some methods implemented in C and others in Python (e.g. through inheritance)?¶
Boost Python库(BPL,http://www.boost.org/libs/python/doc/index.html)提供了一种从C ++(即,你可以继承一个使用BPL用C ++编写的扩展类)。