Skip to content

Commit 2b645a7

Browse files
committed
15.2完成
1 parent d59fe61 commit 2b645a7

File tree

1 file changed

+192
-186
lines changed

1 file changed

+192
-186
lines changed

source/c15/p02_write_simple_c_extension_module.rst

Lines changed: 192 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -5,201 +5,207 @@
55
----------
66
问题
77
----------
8-
You want to write a simple C extension module directly using Python’s extension API
9-
and no other tools.
8+
你想不依靠其他工具,直接使用Python的扩展API来编写一些简单的C扩展模块。
109

1110
|
1211
1312
----------
1413
解决方案
1514
----------
16-
For simple C code, it is straightforward to make a handcrafted extension module. As a
17-
preliminary step, you probably want to make sure your C code has a proper header file.
18-
For example,
19-
20-
/* sample.h */
21-
22-
#include <math.h>
23-
24-
extern int gcd(int, int);
25-
extern int in_mandel(double x0, double y0, int n);
26-
extern int divide(int a, int b, int *remainder);
27-
extern double avg(double *a, int n);
28-
29-
typedef struct Point {
30-
double x,y;
31-
} Point;
32-
33-
extern double distance(Point *p1, Point *p2);
34-
35-
Typically, this header would correspond to a library that has been compiled separately.
36-
With that assumption, here is a sample extension module that illustrates the basics of
37-
writing extension functions:
38-
39-
#include "Python.h"
40-
#include "sample.h"
41-
42-
/* int gcd(int, int) */
43-
static PyObject *py_gcd(PyObject *self, PyObject *args) {
44-
int x, y, result;
45-
46-
if (!PyArg_ParseTuple(args,"ii", &x, &y)) {
47-
return NULL;
48-
}
49-
result = gcd(x,y);
50-
return Py_BuildValue("i", result);
51-
}
52-
53-
/* int in_mandel(double, double, int) */
54-
static PyObject *py_in_mandel(PyObject *self, PyObject *args) {
55-
double x0, y0;
56-
int n;
57-
int result;
58-
59-
if (!PyArg_ParseTuple(args, "ddi", &x0, &y0, &n)) {
60-
return NULL;
61-
}
62-
result = in_mandel(x0,y0,n);
63-
return Py_BuildValue("i", result);
64-
}
65-
66-
/* int divide(int, int, int *) */
67-
static PyObject *py_divide(PyObject *self, PyObject *args) {
68-
int a, b, quotient, remainder;
69-
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
70-
return NULL;
71-
}
72-
quotient = divide(a,b, &remainder);
73-
return Py_BuildValue("(ii)", quotient, remainder);
74-
}
75-
76-
/* Module method table */
77-
static PyMethodDef SampleMethods[] = {
78-
{"gcd", py_gcd, METH_VARARGS, "Greatest common divisor"},
79-
{"in_mandel", py_in_mandel, METH_VARARGS, "Mandelbrot test"},
80-
{"divide", py_divide, METH_VARARGS, "Integer division"},
81-
{ NULL, NULL, 0, NULL}
82-
};
83-
84-
/* Module structure */
85-
static struct PyModuleDef samplemodule = {
86-
PyModuleDef_HEAD_INIT,
87-
88-
"sample", /* name of module */
89-
"A sample module", /* Doc string (may be NULL) */
90-
-1, /* Size of per-interpreter state or -1 */
91-
SampleMethods /* Method table */
92-
};
93-
94-
/* Module initialization function */
95-
PyMODINIT_FUNC
96-
PyInit_sample(void) {
97-
return PyModule_Create(&samplemodule);
98-
}
99-
100-
For building the extension module, create a setup.py file that looks like this:
101-
102-
# setup.py
103-
from distutils.core import setup, Extension
104-
105-
setup(name='sample',
106-
ext_modules=[
107-
Extension('sample',
108-
['pysample.c'],
109-
include_dirs = ['/some/dir'],
110-
define_macros = [('FOO','1')],
111-
undef_macros = ['BAR'],
112-
library_dirs = ['/usr/local/lib'],
113-
libraries = ['sample']
114-
)
115-
]
116-
)
117-
118-
Now, to build the resulting library, simply use python3 buildlib.py build_ext --
119-
inplace. For example:
120-
121-
bash % python3 setup.py build_ext --inplace
122-
running build_ext
123-
building 'sample' extension
124-
gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
125-
-I/usr/local/include/python3.3m -c pysample.c
126-
-o build/temp.macosx-10.6-x86_64-3.3/pysample.o
127-
gcc -bundle -undefined dynamic_lookup
128-
build/temp.macosx-10.6-x86_64-3.3/pysample.o \
129-
-L/usr/local/lib -lsample -o sample.so
130-
bash %
131-
132-
As shown, this creates a shared library called sample.so. When compiled, you should
133-
be able to start importing it as a module:
134-
135-
>>> import sample
136-
>>> sample.gcd(35, 42)
137-
7
138-
>>> sample.in_mandel(0, 0, 500)
139-
1
140-
>>> sample.in_mandel(2.0, 1.0, 500)
141-
142-
0
143-
>>> sample.divide(42, 8)
144-
(5, 2)
145-
>>>
146-
147-
If you are attempting these steps on Windows, you may need to spend some time fiddling
148-
with your environment and the build environment to get extension modules to build
149-
correctly. Binary distributions of Python are typically built using Microsoft Visual
150-
Studio. To get extensions to work, you may have to compile them using the same or
151-
compatible tools. See the Python documentation.
15+
对于简单的C代码,构建一个自定义扩展模块是很容易的。
16+
作为第一步,你需要确保你的C代码有一个正确的头文件。例如:
17+
18+
::
19+
20+
/* sample.h */
21+
22+
#include <math.h>
23+
24+
extern int gcd(int, int);
25+
extern int in_mandel(double x0, double y0, int n);
26+
extern int divide(int a, int b, int *remainder);
27+
extern double avg(double *a, int n);
28+
29+
typedef struct Point {
30+
double x,y;
31+
} Point;
32+
33+
extern double distance(Point *p1, Point *p2);
34+
35+
通常来讲,这个头文件要对应一个已经被单独编译过的库。
36+
有了这些,下面我们演示下编写扩展函数的一个简单例子:
37+
38+
::
39+
40+
#include "Python.h"
41+
#include "sample.h"
42+
43+
/* int gcd(int, int) */
44+
static PyObject *py_gcd(PyObject *self, PyObject *args) {
45+
int x, y, result;
46+
47+
if (!PyArg_ParseTuple(args,"ii", &x, &y)) {
48+
return NULL;
49+
}
50+
result = gcd(x,y);
51+
return Py_BuildValue("i", result);
52+
}
53+
54+
/* int in_mandel(double, double, int) */
55+
static PyObject *py_in_mandel(PyObject *self, PyObject *args) {
56+
double x0, y0;
57+
int n;
58+
int result;
59+
60+
if (!PyArg_ParseTuple(args, "ddi", &x0, &y0, &n)) {
61+
return NULL;
62+
}
63+
result = in_mandel(x0,y0,n);
64+
return Py_BuildValue("i", result);
65+
}
66+
67+
/* int divide(int, int, int *) */
68+
static PyObject *py_divide(PyObject *self, PyObject *args) {
69+
int a, b, quotient, remainder;
70+
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
71+
return NULL;
72+
}
73+
quotient = divide(a,b, &remainder);
74+
return Py_BuildValue("(ii)", quotient, remainder);
75+
}
76+
77+
/* Module method table */
78+
static PyMethodDef SampleMethods[] = {
79+
{"gcd", py_gcd, METH_VARARGS, "Greatest common divisor"},
80+
{"in_mandel", py_in_mandel, METH_VARARGS, "Mandelbrot test"},
81+
{"divide", py_divide, METH_VARARGS, "Integer division"},
82+
{ NULL, NULL, 0, NULL}
83+
};
84+
85+
/* Module structure */
86+
static struct PyModuleDef samplemodule = {
87+
PyModuleDef_HEAD_INIT,
88+
89+
"sample", /* name of module */
90+
"A sample module", /* Doc string (may be NULL) */
91+
-1, /* Size of per-interpreter state or -1 */
92+
SampleMethods /* Method table */
93+
};
94+
95+
/* Module initialization function */
96+
PyMODINIT_FUNC
97+
PyInit_sample(void) {
98+
return PyModule_Create(&samplemodule);
99+
}
100+
101+
要绑定这个扩展模块,像下面这样创建一个 ``setup.py`` 文件:
102+
103+
.. code-block:: python
104+
105+
# setup.py
106+
from distutils.core import setup, Extension
107+
108+
setup(name='sample',
109+
ext_modules=[
110+
Extension('sample',
111+
['pysample.c'],
112+
include_dirs = ['/some/dir'],
113+
define_macros = [('FOO','1')],
114+
undef_macros = ['BAR'],
115+
library_dirs = ['/usr/local/lib'],
116+
libraries = ['sample']
117+
)
118+
]
119+
)
120+
121+
为了构建最终的函数库,只需简单的使用 ``python3 buildlib.py build_ext --inplace`` 命令即可:
122+
123+
::
124+
125+
bash % python3 setup.py build_ext --inplace
126+
running build_ext
127+
building 'sample' extension
128+
gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
129+
-I/usr/local/include/python3.3m -c pysample.c
130+
-o build/temp.macosx-10.6-x86_64-3.3/pysample.o
131+
gcc -bundle -undefined dynamic_lookup
132+
build/temp.macosx-10.6-x86_64-3.3/pysample.o \
133+
-L/usr/local/lib -lsample -o sample.so
134+
bash %
135+
136+
如上所示,它会创建一个名字叫 ``sample.so`` 的共享库。当被编译后,你就能将它作为一个模块导入进来了:
137+
138+
::
139+
140+
>>> import sample
141+
>>> sample.gcd(35, 42)
142+
7
143+
>>> sample.in_mandel(0, 0, 500)
144+
1
145+
>>> sample.in_mandel(2.0, 1.0, 500)
146+
147+
0
148+
>>> sample.divide(42, 8)
149+
(5, 2)
150+
>>>
151+
152+
如果你是在Windows机器上面尝试这些步骤,可能会遇到各种环境和编译问题,你需要花更多点时间去配置。
153+
Python的二进制分发通常使用了Microsoft Visual Studio来构建。
154+
为了让这些扩展能正常工作,你需要使用同样或兼容的工具来编译它。
155+
参考相应的 `Python文档 <https://docs.python.org/3/extending/windows.html>`_
152156

153157
|
154158
155159
----------
156160
讨论
157161
----------
158-
Before attempting any kind of handwritten extension, it is absolutely critical that you
159-
consult Python’s documentation on “Extending and Embedding the Python Interpret‐
160-
er”. Python’s C extension API is large, and repeating all of it here is simply not practical.
161-
However, the most important parts can be easily discussed.
162-
First, in extension modules, functions that you write are all typically written with a
163-
common prototype such as this:
164-
165-
static PyObject *py_func(PyObject *self, PyObject *args) {
166-
...
167-
}
168-
169-
PyObject is the C data type that represents any Python object. At a very high level, an
170-
extension function is a C function that receives a tuple of Python objects (in PyObject
171-
*args) and returns a new Python object as a result. The self argument to the function
172-
is unused for simple extension functions, but comes into play should you want to define
173-
new classes or object types in C (e.g., if the extension function were a method of a class,
174-
then self would hold the instance).
175-
The PyArg_ParseTuple() function is used to convert values from Python to a C rep‐
176-
resentation. As input, it takes a format string that indicates the required values, such as
177-
“i” for integer and “d” for double, as well as the addresses of C variables in which to place
178-
the converted results. PyArg_ParseTuple() performs a variety of checks on the number
179-
and type of arguments. If there is any mismatch with the format string, an exception is
180-
raised and NULL is returned. By checking for this and simply returning NULL, an ap‐
181-
propriate exception will have been raised in the calling code.
182-
The Py_BuildValue() function is used to create Python objects from C data types. It
183-
also accepts a format code to indicate the desired type. In the extension functions, it is
184-
used to return results back to Python. One feature of Py_BuildValue() is that it can
185-
build more complicated kinds of objects, such as tuples and dictionaries. In the code
186-
for py_divide(), an example showing the return of a tuple is shown. However, here are
187-
a few more examples:
188-
189-
return Py_BuildValue("i", 34); // Return an integer
190-
return Py_BuildValue("d", 3.4); // Return a double
191-
return Py_BuildValue("s", "Hello"); // Null-terminated UTF-8 string
192-
return Py_BuildValue("(ii)", 3, 4); // Tuple (3, 4)
193-
194-
Near the bottom of any extension module, you will find a function table such as the
195-
SampleMethods table shown in this recipe. This table lists C functions, the names to use
196-
in Python, as well as doc strings. All modules are required to specify such a table, as it
197-
gets used in the initialization of the module.
198-
The final function PyInit_sample() is the module initialization function that executes
199-
when the module is first imported. The primary job of this function is to register the
200-
module object with the interpreter.
201-
As a final note, it must be stressed that there is considerably more to extending Python
202-
with C functions than what is shown here (in fact, the C API contains well over 500
203-
functions in it). You should view this recipe simply as a stepping stone for getting started.
204-
To do more, start with the documentation on the PyArg_ParseTuple() and Py_Build
205-
Value() functions, and expand from there.
162+
在尝试任何手写扩展之前,最好能先参考下Python文档中的
163+
`扩展和嵌入Python解释器 <https://docs.python.org/3/extending/index.html>`_ .
164+
Python的C扩展API很大,在这里整个去讲述它没什么实际意义。
165+
不过对于最核心的部分还是可以讨论下的。
166+
167+
首先,在扩展模块中,你写的函数都是像下面这样的一个普通原型:
168+
169+
::
170+
171+
static PyObject *py_func(PyObject *self, PyObject *args) {
172+
...
173+
}
174+
175+
``PyObject`` 是一个能表示任何Python对象的C数据类型。
176+
在一个高级层面,一个扩展函数就是一个接受一个Python对象
177+
(在 PyObject *args中)元组并返回一个新Python对象的C函数。
178+
函数的 ``self`` 参数对于简单的扩展函数没有被使用到,
179+
不过如果你想定义新的类或者是C中的对象类型的话就能派上用场了。比如如果扩展函数是一个类的一个方法,
180+
那么 ``self`` 就能引用那个实例了。
181+
182+
``PyArg_ParseTuple()`` 函数被用来将Python中的值转换成C中对应表示。
183+
它接受一个指定输入格式的格式化字符串作为输入,比如“i”代表整数,“d”代表双精度浮点数,
184+
同样还有存放转换后结果的C变量的地址。
185+
如果输入的值不匹配这个格式化字符串,就会抛出一个异常并返回一个NULL值。
186+
通过检查并返回NULL,一个合适的异常会在调用代码中被抛出。
187+
188+
``Py_BuildValue()`` 函数被用来根据C数据类型创建Python对象。
189+
它同样接受一个格式化字符串来指定期望类型。
190+
在扩展函数中,它被用来返回结果给Python。
191+
``Py_BuildValue()`` 的一个特性是它能构建更加复杂的对象类型,比如元组和字典。
192+
在 ``py_divide()`` 代码中,一个例子演示了怎样返回一个元组。不过,下面还有一些实例:
193+
194+
::
195+
196+
return Py_BuildValue("i", 34); // Return an integer
197+
return Py_BuildValue("d", 3.4); // Return a double
198+
return Py_BuildValue("s", "Hello"); // Null-terminated UTF-8 string
199+
return Py_BuildValue("(ii)", 3, 4); // Tuple (3, 4)
200+
201+
在扩展模块底部,你会发现一个函数表,比如本节中的 ``SampleMethods`` 表。
202+
这个表可以列出C函数、Python中使用的名字、文档字符串。
203+
所有模块都需要指定这个表,因为它在模块初始化时要被使用到。
204+
205+
最后的函数 ``PyInit_sample()`` 是模块初始化函数,但该模块第一次被导入时执行。
206+
这个函数的主要工作是在解释器中注册模块对象。
207+
208+
最后一个要点需要提出来,使用C函数来扩展Python要考虑的事情还有很多,本节只是一小部分。
209+
(实际上,C API包含了超过500个函数)。你应该将本节当做是一个入门篇。
210+
更多高级内容,可以看看 ``PyArg_ParseTuple()`` 和 ``Py_BuildValue()`` 函数的文档,
211+
然后进一步扩展开。

0 commit comments

Comments
 (0)