Extending/Embedding FAQ

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解释器调用的函数之前放置extern “C” 使用构造函数的全局或静态C ++对象可能不是一个好主意。

Writing C is hard; are there any alternatives?

有许多替代方法来编写自己的C扩展,这取决于你想要做什么。

Cython及其相对的Pyrex是接受稍微修改的Python形式并生成相应C代码的编译器。Cython和Pyrex使编写扩展成为可能,而不必学习Python的C API。

如果需要连接到一些没有Python扩展的C或C ++库,可以尝试使用诸如SWIG等工具包装库的数据类型和函数。SIPCXX BoostWeave也是用于包装C ++库的替代方法。

How can I execute arbitrary Python statements from C?

执行此操作的最高级函数是PyRun_SimpleString(),它接受要在模块__main__的上下文中执行的单个字符串参数,并返回0表示成功,-1当发生异常(包括SyntaxError)时。如果你想要更多的控制,使用PyRun_String();请参阅Python/pythonrun.cPyRun_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.stdoutsys.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.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;
}

另一个解决方案是尝试使用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)?

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

Boost Python库(BPL,http://www.boost.org/libs/python/doc/index.html)提供了一种从C ++(即,你可以继承一个使用BPL用C ++编写的扩展类)。