扩展/嵌入常见问题

可以使用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 等工具包装库的数据类型和函数。 SIPCXX Boost , 或 Weave 也是包装C ++库的替代方案。

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

The highest-level function to do this is PyRun_SimpleString() which takes a single string argument to be executed in the context of the module __main__ and returns 0 for success and -1 when an exception occurred (including SyntaxError). If you want more control, use PyRun_String(); see the source for PyRun_SimpleString() in Python/pythonrun.c.

如何从C中评估任意Python表达式?

Call the function PyRun_String() from the previous question with the start symbol Py_eval_input; it parses an expression, evaluates it and returns its value.

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

That depends on the object’s type. If it’s a tuple, PyTuple_Size() returns its length and PyTuple_GetItem() returns the item at a specified index. Lists have similar functions, PyListSize() and PyList_GetItem().

For bytes, PyBytes_Size() returns its length and PyBytes_AsStringAndSize() provides a pointer to its value and its length. Note that Python bytes objects may contain null bytes so C’s strlen() should not be used.

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

There is also a high-level API to Python objects which is provided by the so-called ‘abstract’ interface – read Include/abstract.h for further details. It allows interfacing with any kind of Python sequence using calls like PySequence_Length(), PySequence_GetItem(), etc. as well as many other useful protocols such as numbers (PyNumber_Index() et al.) and mappings in the PyMapping APIs.

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

The PyObject_CallMethod() function can be used to call an arbitrary method of an object. The parameters are the object, the name of the method to call, a format string like that used with Py_BuildValue(), and the argument values:

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

This works for any object that has methods – whether built-in or user-defined. You are responsible for eventually Py_DECREF()’ing the return value.

To call, e.g., a file object’s “seek” method with arguments 10, 0 (assuming the file object pointer is “f”):

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

Note that since PyObject_CallObject() always wants a tuple for the argument list, to call a function without arguments, pass “()” for the format, and to call a function with one argument, surround the argument in parentheses, e.g. “(i)”.

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

In Python code, define an object that supports the write() method. Assign this object to sys.stdout and sys.stderr. Call print_error, or just allow the standard traceback mechanism to work. Then, the output will go wherever your write() method sends it.

The easiest way to do this is to use the io.StringIO class:

>>> import io, sys
>>> sys.stdout = io.StringIO()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(sys.stdout.getvalue())
foo
hello world!

A custom object to do the same would look like this:

>>> 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编写的模块?

You can get a pointer to the module object as follows:

module = PyImport_ImportModule("<modulename>");

If the module hasn’t been imported yet (i.e. it is not yet present in sys.modules), this initializes the module; otherwise it simply returns the value of sys.modules["<modulename>"]. Note that it doesn’t enter the module into any namespace – it only ensures it has been initialized and is stored in sys.modules.

You can then access the module’s attributes (i.e. any name defined in the module) as follows:

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

Calling PyObject_SetAttrString() to assign to variables in the module also works.

如何从Python接口到C ++对象?

Depending on your requirements, there are many approaches. To do this manually, begin by reading the “Extending and Embedding” document. Realize that for the Python run-time system, there isn’t a whole lot of difference between C and C++ – so the strategy of building a new Python type around a C structure (pointer) type will also work for C++ objects.

For C++ libraries, see 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.cParser/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;
}

Another solution is trying to compile the received string with Py_CompileString(). If it compiles without errors, try to execute the returned code object by calling PyEval_EvalCode(). Otherwise save the input for later. If the compilation fails, find out if it’s an error or just more input is required - by extracting the message string from the exception tuple and comparing it to the string “unexpected EOF while parsing”. Here is a complete example using the GNU readline library (you may want to ignore SIGINT while calling readline()):

#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);
}

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

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

能否创建一个对象类,其中部分方法在C中实现,而其他方法在Python中实现(例如通过继承)?

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

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