Extending/Embedding FAQ

Contents

我可以在 C 中创建自己的函数吗?

是的,您可以在 C 中创建包含函数,变量,异常甚至新类型的内置模块。文档扩展和嵌入 Python 解释器中对此进行了说明。

大多数中级或高级 Python 书籍也将涵盖此主题。

我可以在 C 中创建自己的函数吗?

是的,使用 C 中提供的 C 兼容性Function。将extern "C" { ... }放在 Python 包含文件周围,并将extern "C"放在将要由 Python 解释器调用的每个函数之前。带有构造函数的全局或静态 C 对象可能不是一个好主意。

编写 C 语言很困难;还有其他选择吗?

根据您要try执行的操作,可以使用多种方法来编写自己的 C 扩展。

如果需要更高的速度,则Psyco从 Python 字节码生成 x86 汇编代码。只要您在具有 x86 兼容处理器的计算机上运行,就可以使用 Psyco 编译代码中对时间要求最严格的函数,并且只需花费很少的精力即可获得显着的改进。

Cython及其相对的Pyrex是接受稍微修改形式的 Python 并生成相应 C 代码的编译器。 Pyrex 使得无需学习 Python 的 C API 即可编写扩展。

如果您需要接口到当前不存在 Pythonextensions 的 C 或 C 库,则可以try使用SWIG之类的工具包装该库的数据类型和函数。 SIPCXX BoostWeave也是包装 C 库的替代方法。

如何从 C 执行任意 Python 语句?

最高级别的函数是PyRun_SimpleString(),该函数接受一个要在模块__main__上下文中执行的字符串参数,并返回 0 以表示成功,返回-1 则表示发生异常(包括SyntaxError)。如果需要更多控制,请使用PyRun_String();请参见Python/pythonrun.cPyRun_SimpleString()的来源。

如何从 C 计算任意 Python 表达式?

使用上一个符号Py_eval_input从上一个问题中调用函数PyRun_String();它解析一个表达式,对其求值并返回其值。

如何从 Python 对象提取 C 值?

这取决于对象的类型。如果是 Tuples,则PyTuple_Size()返回其长度,而PyTuple_GetItem()返回指定索引处的项目。列表具有相似的FunctionPyListSize()PyList_GetItem()

对于字符串,PyString_Size()返回其长度,而PyString_AsString()指向其值的指针。请注意,Python 字符串可能包含空字节,因此不应使用 C 的strlen()

要测试对象的类型,请首先确保它不是* NULL *,然后使用PyString_Check()PyTuple_Check()PyList_Check()等。

Python 对象还有一个高级 API,该 API 由所谓的“抽象”接口提供-有关详细信息,请阅读Include/abstract.h。它允许使用诸如PySequence_Length()PySequence_GetItem()等的调用以及许多其他有用的协议来与任何种类的 Python 序列进行交互。

如何使用 Py_BuildValue()创建任意长度的 Tuples?

你不能使用t = PyTuple_New(n)代替,并使用PyTuple_SetItem(t, i, o)填充对象–请注意,这“消耗”了o的引用计数,因此您必须Py_INCREF()。列表具有类似的FunctionPyList_New(n)PyList_SetItem(l, i, o)。请注意,在将 Tuples 传递给 Python 代码之前,必须将所有 Tuples 项设置为某个值-PyTuple_New(n)将它们初始化为 NULL,这不是有效的 Python 值。

如何从 C 调用对象的方法?

PyObject_CallMethod()函数可用于调用对象的任意方法。参数是对象,要调用的方法的名称,与Py_BuildValue()一起使用的格式字符串以及参数值:

PyObject *
PyObject_CallMethod(PyObject *object, char *method_name,
                    char *arg_format, ...);

此方法适用于具有方法的任何对象-内置还是用户定义的。您有责任finallyPy_DECREF()'获得返回值。

例如,以参数 10、0 调用文件对象的“ seek”方法(假设文件对象指针为“ f”):

res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
        ... an exception occurred ...
}
else {
        Py_DECREF(res);
}

请注意,由于PyObject_CallObject() 始终要为参数列表使用 Tuples,因此要调用不带参数的函数,请将“()”传递给格式,并调用带有一个参数的函数,请将参数用括号括起来,例如“(一世)”。

如何捕获 PyErr_Print()(或打印到 stdout/stderr 的任何内容)的输出?

在 Python 代码中,定义一个支持write()方法的对象。将此对象分配给sys.stdoutsys.stderr。调用 print_error,或仅允许标准回溯机制起作用。然后,无论您的write()方法将其发送到哪里,输出都将到达。

最简单的方法是使用标准库中的 StringIO 类。

示例代码并用于捕获标准输出:

>>> class StdoutCatcher:
...     def __init__(self):
...         self.data = ''
...     def write(self, stuff):
...         self.data = self.data + stuff
...
>>> import sys
>>> sys.stdout = StdoutCatcher()
>>> print 'foo'
>>> print 'hello world!'
>>> sys.stderr.write(sys.stdout.data)
foo
hello world!

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

您可以按以下方式获取指向模块对象的指针:

module = PyImport_ImportModule("<modulename>");

如果尚未导入模块(即sys.modules中尚不存在),则会初始化模块;否则,它仅返回sys.modules["<modulename>"]的值。请注意,它不会将模块 Importing 任何名称空间–仅确保模块已初始化并存储在sys.modules中。

然后,您可以按以下方式访问模块的属性(即模块中定义的任何名称):

attr = PyObject_GetAttrString(module, "<attrname>");

调用PyObject_SetAttrString()分配给模块中的变量也可以。

如何从 Python 连接到 C 对象?

根据您的要求,有很多方法。要手动执行此操作,请先阅读“扩展和嵌入”文档。意识到对于 Python 运行时系统,C 和 C 之间没有太多区别–因此围绕 C 结构(指针)类型构建新 Python 类型的策略也适用于 C 对象。

有关 C 库,请参见写 C 很难。还有其他选择吗?

我使用安装文件添加了一个模块,但制作失败;为什么?

安装程序必须以换行符结尾,如果那里没有换行符,则构建过程将失败。 (解决此问题需要一些丑陋的 shell 脚本黑客攻击,并且此错误非常小,因此似乎不值得这样做.)

如何调试扩展程序?

当将 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

“ SystemError:_PyImport_FixupExtension:模块未加载模块”是什么意思?

这意味着您已经创建了一个名为“ yourmodule”的扩展模块,但是您的模块 init 函数未使用该名称进行初始化。

每个模块的 init 函数都有一行类似于:

module = Py_InitModule("yourmodule", yourmodule_functions);

如果传递给此函数的字符串与扩展模块的名称不同,则将引发SystemError异常。

如何从“无效 Importing”中区分“Importing 不完整”?

有时您想模拟 Python 交互式解释器的行为,当 Importing 不完整时(例如,您键入了“ if”语句的开头,或者您没有关闭括号或三元字符串引号),它会向您提供 continue 提示。当 Importing 无效时,它会立即为您提供语法错误消息。

在 Python 中,您可以使用codeop模块,该模块可以充分近似解析器的行为。例如,IDLE 使用此Function。

在 C 语言中最简单的方法是调用PyRun_InteractiveLoop()(可能在单独的线程中),然后让 Python 解释器为您处理 Importing。您还可以设置PyOS_ReadlineFunctionPointer()指向自定义 Importing Function。有关更多提示,请参见Modules/readline.cParser/myreadline.c

但是,有时您必须在与其余应用程序相同的线程中运行嵌入式 Python 解释器,并且在 await 用户 Importing 时不允许PyRun_InteractiveLoop()停止。然后,一种解决方案是调用PyParser_ParseString()并测试e.error等于E_EOF,这意味着 Importing 不完整。这是未经测试的示例代码片段,其灵感来自 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;
}

另一种解决方案是try使用Py_CompileString()编译接收到的字符串。如果编译没有错误,请trypass调用PyEval_EvalCode()执行返回的代码对象。否则,请保存 Importing 以供以后使用。如果编译失败,则找出是错误还是需要更多 Importing-pass从异常 Tuples 中提取消息字符串并将其与“解析时意外的 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 ((PyCodeObject *)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);
}

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

要动态加载 g 扩展模块,您必须重新编译 Python,使用 g 重新链接它(更改 Python Modules Makefile 中的 LINKCC),并使用 g 链接扩展模块(例如g++ -shared -o mymodule.so mymodule.o)。

我可以使用在 C 中实现的某些方法和在 Python 中实现的其他方法(例如,pass继承)创建对象类吗?

是的,您可以继承内置类,例如intlistdict等。

Boost Python 库(BPL,http://www.boost.org/libs/python/doc/index.html)提供了一种从 C 执行此操作的方法(即,您可以使用 BPL 从用 C 编写的扩展类中继承)。

导入模块 X 时,为什么会显示“未定义符号:PyUnicodeUCS2 *”?

您使用的 Python 版本使用 Unicode 字符使用 4 字节表示,但是要导入的某些 C 扩展模块是使用使用 Unicode 字符使用 2 字节表示的 Python 编译的。

相反,如果未定义符号的名称以PyUnicodeUCS4开头,那么问题就相反了:Python 是使用 2 字节 Unicode 字符构建的,而扩展模块是使用 4 字节 Unicode 字符的 Python 编译的。

使用预构建的扩展包时,很容易发生这种情况。特别是 RedHat Linux 7.x,提供了使用 4 字节 Unicode 编译的“ python2”二进制文件。如果扩展使用任何PyUnicode_*()函数,这只会导致链接失败。如果扩展对Py_BuildValue()(或类似名称)使用任何与 Unicode 相关的格式说明符或对PyArg_ParseTuple()使用参数说明,也将引起问题。

您可以pass检查 sys.maxunicode 的值来检查 Python 解释程序正在使用的 Unicode 字符的大小:

>>> import sys
>>> if sys.maxunicode > 65535:
...     print 'UCS4 build'
... else:
...     print 'UCS2 build'

解决此问题的唯一方法是使用扩展模块,该扩展模块是使用对 Unicode 字符使用相同大小构建的 Python 二进制文件编译的。