扩展/嵌入常见问题
*****************


可以使用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语句？
===========================

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.


如何使用Py_BuildValue()创建任意长度的元组？
===========================================

不可以。应该使用 "PyTuple_Pack()" 。


如何从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.

有关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 <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”
进行比较。下面是使用GNUreadline库的完整示例(您可能希望在调用readline()
时忽略 **SIGINT** )：

   #include <stdio.h>
   #include <readline.h>

   #define PY_SSIZE_T_CLEAN
   #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中实现（例如通过继承）？
=====================================================================================

是的，您可以继承内置类，例如 "int" ， "list" ， "dict" 等。

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