c++ 调用python_Python(43)常见问题:扩展/嵌入常见问题

扩展/嵌入常见问题

目录
  • 扩展/嵌入常见问题
    • 可以使用 C 语言创建自己的函数吗?
    • 可以使用 C++ 语言创建自己的函数吗?
    • C很难写,有没有其他选择?
    • 如何在 C 中执行任意 Python 语句?
    • 如何在 C 中对任意 Python 表达式求值?
    • 如何从Python对象中提取C的值?
    • 如何使用Py_BuildValue()创建任意长度的元组?
    • 如何从C调用对象的方法?
    • 如何捕获PyErr_Print()(或打印到stdout / stderr的任何内容)的输出?
    • 如何从C访问用Python编写的模块?
    • 如何在 Python 中对接 C ++ 对象?
    • 我使用Setup文件添加了一个模块,为什么make失败了?
    • 如何调试扩展?
    • 我想在Linux系统上编译一个Python模块,但是缺少一些文件。为什么?
    • 如何区分“输入不完整”和“输入无效”?
    • 如何找到未定义的g++符号__builtin_new或__pure_virtual?
    • 能否创建一个对象类,其中部分方法在C中实现,而其他方法在Python中实现(例如通过继承)?

可以使用 C 语言创建自己的函数吗?

是的,您可以在C中创建包含函数、变量、异常甚至新类型的内置模块。在文档 扩展和嵌入 Python 解释器 中有说明。大多数中级或高级的Python书籍也涵盖这个主题。

可以使用 C++ 语言创建自己的函数吗?

是的,可以使用C ++中兼容C的功能。在Python include文件周围放置`  extern“C”{...}` ,并在Python解释器调用的每个函数之前放置  extern“C” 。具有构造函数的全局或静态C ++对象可能不是一个好主意。

C很难写,有没有其他选择?

编写自己的C扩展有很多选择,具体取决于您要做的事情。Cython 及其相关的 Pyrex 是接受稍微修改过的Python形式并生成相应C代码的编译器。Cython和Pyrex可以编写扩展而无需学习Python的C API。如果需要连接到某些当前不存在Python扩展的C或C ++库,可以尝试使用 SWIG 等工具包装库的数据类型和函数。 SIP , CXX Boost , 或 Weave 也是包装C ++库的替代方案。

如何在 C 中执行任意 Python 语句?

执行此操作的最高层级函数为  PyRun_SimpleString(),它接受单个字符串参数用于在模块  __main__ 的上下文中执行并在成功时返回  0 而在发生异常 (包括  SyntaxError) 时返回  -1。如果你想要更多可控性,可以使用  PyRun_String();请在  Python/pythonrun.c 中查看  PyRun_SimpleString() 的源码。

如何在 C 中对任意 Python 表达式求值?

可以调用前一问题中介绍的函数  PyRun_String() 并附带起始标记符  Py_eval_input;它会解析表达式,对其求值并返回结果值。

如何从Python对象中提取C的值?

这取决于对象的类型。如果是元组, PyTuple_Size() 可返回其长度而  PyTuple_GetItem() 可返回指定序号上的项。对于列表也有类似的函数  PyListSize() 和  PyList_GetItem()。对于字节串, PyBytes_Size() 可返回其长度而  PyBytes_AsStringAndSize() 提供一个指向其值和长度的指针。请注意 Python 字节串可能为空,因此 C 的  strlen() 不应被使用。要检测一个对象的类型,首先要确保它不为  NULL,然后使用  PyBytes_Check()PyTuple_Check()PyList_Check() 等等。还有一个针对 Python 对象的高层级 API,通过所谓的‘抽象’接口提供 —— 请参阅  Include/abstract.h 了解详情。它允许使用  PySequence_Length()PySequence_GetItem() 这样的调用来与任意种类的 Python 序列进行对接,此外还可使用许多其他有用的协议例如数字 ( PyNumber_Index() 等) 以及 PyMapping API 中的各种映射等等。

如何使用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!

如何从C访问用Python编写的模块?

你可以通过如下方式获得一个指向模块对象的指针:
module = PyImport_ImportModule("");
如果模块尚未被导入(即它还不存在于  sys.modules 中),这会初始化该模块;否则它只是简单地返回  sys.modules[""] 的值。请注意它并不会将模块加入任何命名空间 —— 它只是确保模块被初始化并存在于  sys.modules 中。之后你就可以通过如下方式来访问模块的属性(即模块中定义的任何名称):
attr = PyObject_GetAttrString(module, "");
调用  PyObject_SetAttrString() 为模块中的变量赋值也是可以的。

如何在 Python 中对接 C ++ 对象?

根据你的需求,可以选择许多方式。手动的实现方式请查阅 "扩展与嵌入" 文档 来入门。需要知道的是对于 Python 运行时系统来说,C 和 C++ 并不没有太大的区别 —— 因此围绕一个 C 结构(指针)类型构建新 Python 对象的策略同样适用于 C++ 对象。有关C ++库,请参阅 C很难写,有没有其他选择?

我使用Setup文件添加了一个模块,为什么make失败了?

安装程序必须以换行符结束,如果没有换行符,则构建过程将失败。(修复这个需要一些丑陋的shell脚本编程,而且这个bug很小,看起来不值得花这么大力气。)

如何调试扩展?

将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

我想在Linux系统上编译一个Python模块,但是缺少一些文件。为什么?

大多数打包的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 。但是,有时必须在与其他应用程序相同的线程中运行嵌入式Python解释器,并且不能允许  PyRun_InteractiveLoop() 在等待用户输入时停止。那么另一个解决方案是调用  PyParser_ParseString() 并测试  e.error 等于  E_EOF ,如果等于,就意味着输入不完整。这是一个示例代码片段,未经测试,灵感来自Alex Farber的代码:
#define PY_SSIZE_T_CLEAN#include #include #include #include #include #include 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” 进行比较。下面是使用GNUreadline库的完整示例(您可能希望在调用readline()时忽略  SIGINT ):
#include #include #define PY_SSIZE_T_CLEAN#include #include #include #include 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, "", 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);
}

如何找到未定义的g++符号__builtin_new或__pure_virtual?

要动态加载g ++扩展模块,必须重新编译Python,要使用g ++重新链接(在Python Modules Makefile中更改LINKCC),及链接扩展模块(例如:  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 ++执行此操作的方法(即,您可以使用BPL继承自C ++编写的扩展类 )。

9a80aa93b130b820a601e58ea0e34262.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要在 C 程序中调用 Python 程序,需要使用 Python/C API(Application Programming Interface,应用程序编程接口)。 首先,在 C 程序中包含 Python.h 头文件。然后,调用 Py_Initialize 函数来初始化 Python 解释器。接下来,可以使用 PyRun_SimpleString 函数来执行 Python 代码,或者使用 PyImport_ImportModule 函数来导入 Python 模块,并调用模块中的函数。 例如,下面是一个简单的 C 程序,它调用 Python 代码计算两个数的和: ``` #include <Python.h> #include <stdio.h> int main() { Py_Initialize(); PyRun_SimpleString("x = 1 + 2"); Py_Finalize(); return 0; } ``` 这个程序会初始化 Python 解释器,然后执行 Python 代码 "x = 1 + 2",最后关闭 Python 解释器。 注意,在调用 Python 代码之前,需要先初始化 Python 解释器,在结束时需要调用 Py_Finalize 函数来关闭 Python 解释器。 有关如何使用 Python/C API 的更多信息,可以参考 Python 官方文档:https://docs.python.org/3/c-api/index.html。 ### 回答2: 要在C语言中调用Python,可以使用Python的扩展库来实现。 一种常见的方法是使用Python的C扩展 API。首先,需要在C代码中包含Python.h头文件。然后,可以使用Py_Initialize()函数初始化Python解释器。接下来,可以使用PyRun_SimpleString()函数来执行Python代码,或者使用PyRun_File()函数来执行Python脚本文件。还可以使用PyImport_Import()函数来导入Python模块,并使用PyCallable_Check()函数来检查模块中的函数是否可调用。通过PyEval_CallObject()函数可以调用Python函数,并传递参数和接收返回值。最后,使用Py_Finalize()函数来结束Python解释器的使用。 另一个方法是使用Python的Cython库。Cython可以将Python代码转换为C代码,从而实现C和Python的无缝集成。通过使用Cython,可以在C代码中调用Python函数,无需显式调用Python解释器。 除了上述两种方法,还可以使用其他第三方库,如Boost.Python和CFFI。这些库可以帮助在C代码中调用Python,并提供更加简单和高级的接口。 总之,要在C语言中调用Python,可以使用Python的C扩展 API、Cython库或其他第三方库来实现。这样可以将C和Python的功能结合起来,实现更加灵活和高效的应用程序。 ### 回答3: C 调用 Python 是一种将 C 代码与 Python 代码进行互操作的方法。C 是一种底层的编程语言,而 Python 是一种高层的解释型脚本语言。通过 C 调用 Python,我们可以在 C 代码中直接使用 Python 提供的功能。 C 调用 Python 的过程可以分为以下几个步骤: 1. 首先,需要将 Python 的头文件包含在 C 代码中,以便在 C 代码中使用 Python 提供的函数和数据结构。 2. 其次,需要初始化 Python 解释器。这可以通过调用 `Py_Initialize` 函数来完成。该函数会初始化解释器环境,并加载 Python 的内置模块和库。 3. 接下来,可以使用 `PyRun_SimpleString` 函数或 `PyRun_SimpleFile` 函数来执行 Python 代码。`PyRun_SimpleString` 函数用于执行字符串形式的 Python 代码,而 `PyRun_SimpleFile` 函数用于执行存储在文件中的 Python 代码。 4. 在执行完 Python 代码后,可以使用 `Py_Finalize` 函数来关闭 Python 解释器。这会释放解释器占用的资源,并进行一些清理工作。 通过以上步骤,我们可以在 C 代码中调用 Python 提供的函数、使用 Python 的数据结构,甚至将 C 代码嵌入到 Python 程序中。 C 调用 Python 在某些场景下非常有用,例如当我们需要利用 Python 强大的库来完成某些特定任务,但同时希望保持底层代码的高效性时。这种方法能够将 C 的性能优势和 Python 的灵活性结合起来,提供更好的开发体验和性能表现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值