Skip to content

Commit b9a4fa7

Browse files
committed
15.1进行中
1 parent 88765c0 commit b9a4fa7

File tree

1 file changed

+133
-132
lines changed

1 file changed

+133
-132
lines changed

source/c15/p01_access_ccode_using_ctypes.rst

Lines changed: 133 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -5,154 +5,155 @@
55
----------
66
问题
77
----------
8-
You have a small number of C functions that have been compiled into a shared library
9-
or DLL. You would like to call these functions purely from Python without having to
10-
write additional C code or using a third-party extension tool.
8+
你有一些C函数已经被编译到共享库或DLL中。你希望可以使用纯Python代码调用这些函数,
9+
而不用编写额外的C代码或使用第三方扩展工具。
1110

1211
|
1312
1413
----------
1514
解决方案
1615
----------
17-
For small problems involving C code, it is often easy enough to use the ctypes module
18-
that is part of Python’s standard library. In order to use ctypes, you must first make
19-
sure the C code you want to access has been compiled into a shared library that is
20-
compatible with the Python interpreter (e.g., same architecture, word size, compiler,
21-
etc.). For the purposes of this recipe, assume that a shared library, libsample.so, has
22-
been created and that it contains nothing more than the code shown in the chapter
23-
introduction. Further assume that the libsample.so file has been placed in the same
24-
directory as the sample.py file shown next.
25-
To access the resulting library, you make a Python module that wraps around it, such
26-
as the following:
27-
# sample.py
28-
import ctypes
29-
import os
30-
31-
# Try to locate the .so file in the same directory as this file
32-
_file = 'libsample.so'
33-
_path = os.path.join(*(os.path.split(__file__)[:-1] + (_file,)))
34-
_mod = ctypes.cdll.LoadLibrary(_path)
35-
36-
# int gcd(int, int)
37-
gcd = _mod.gcd
38-
gcd.argtypes = (ctypes.c_int, ctypes.c_int)
39-
gcd.restype = ctypes.c_int
40-
41-
# int in_mandel(double, double, int)
42-
in_mandel = _mod.in_mandel
43-
in_mandel.argtypes = (ctypes.c_double, ctypes.c_double, ctypes.c_int)
44-
in_mandel.restype = ctypes.c_int
45-
46-
# int divide(int, int, int *)
47-
_divide = _mod.divide
48-
_divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int))
49-
_divide.restype = ctypes.c_int
50-
51-
def divide(x, y):
52-
rem = ctypes.c_int()
53-
quot = _divide(x, y, rem)
54-
55-
return quot,rem.value
56-
57-
# void avg(double *, int n)
58-
# Define a special type for the 'double *' argument
59-
class DoubleArrayType:
60-
def from_param(self, param):
61-
typename = type(param).__name__
62-
if hasattr(self, 'from_' + typename):
63-
return getattr(self, 'from_' + typename)(param)
64-
elif isinstance(param, ctypes.Array):
65-
return param
66-
else:
67-
raise TypeError("Can't convert %s" % typename)
68-
69-
# Cast from array.array objects
70-
def from_array(self, param):
71-
if param.typecode != 'd':
72-
raise TypeError('must be an array of doubles')
73-
ptr, _ = param.buffer_info()
74-
return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_double))
75-
76-
# Cast from lists/tuples
77-
def from_list(self, param):
78-
val = ((ctypes.c_double)*len(param))(*param)
79-
return val
80-
81-
from_tuple = from_list
82-
83-
# Cast from a numpy array
84-
def from_ndarray(self, param):
85-
return param.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
86-
87-
DoubleArray = DoubleArrayType()
88-
_avg = _mod.avg
89-
_avg.argtypes = (DoubleArray, ctypes.c_int)
90-
_avg.restype = ctypes.c_double
91-
92-
def avg(values):
93-
return _avg(values, len(values))
94-
95-
# struct Point { }
96-
class Point(ctypes.Structure):
97-
_fields_ = [('x', ctypes.c_double),
98-
('y', ctypes.c_double)]
99-
100-
# double distance(Point *, Point *)
101-
distance = _mod.distance
102-
distance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point))
103-
distance.restype = ctypes.c_double
16+
对于需要调用C代码的一些小的问题,通常使用Python标准库中的 ``ctypes`` 模块就足够了。
17+
要使用 ``ctypes`` ,你首先要确保你要访问的C代码已经被编译到和Python解释器兼容
18+
(同样的架构、字大小、编译器等)的某个共享库中了。
19+
为了进行本节的演示,假设你有一个共享库名字叫 ``libsample.so`` ,里面的内容就是15章介绍部分那样。
20+
另外还假设这个 ``libsample.so`` 文件被放置到位于 ``sample.py`` 文件相同的目录中了。
21+
22+
要访问这个函数库,你要先构建一个包装它的Python模块,如下这样:
23+
24+
.. code-block:: python
25+
26+
# sample.py
27+
import ctypes
28+
import os
29+
30+
# Try to locate the .so file in the same directory as this file
31+
_file = 'libsample.so'
32+
_path = os.path.join(*(os.path.split(__file__)[:-1] + (_file,)))
33+
_mod = ctypes.cdll.LoadLibrary(_path)
34+
35+
# int gcd(int, int)
36+
gcd = _mod.gcd
37+
gcd.argtypes = (ctypes.c_int, ctypes.c_int)
38+
gcd.restype = ctypes.c_int
39+
40+
# int in_mandel(double, double, int)
41+
in_mandel = _mod.in_mandel
42+
in_mandel.argtypes = (ctypes.c_double, ctypes.c_double, ctypes.c_int)
43+
in_mandel.restype = ctypes.c_int
44+
45+
# int divide(int, int, int *)
46+
_divide = _mod.divide
47+
_divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int))
48+
_divide.restype = ctypes.c_int
49+
50+
def divide(x, y):
51+
rem = ctypes.c_int()
52+
quot = _divide(x, y, rem)
53+
54+
return quot,rem.value
55+
56+
# void avg(double *, int n)
57+
# Define a special type for the 'double *' argument
58+
class DoubleArrayType:
59+
def from_param(self, param):
60+
typename = type(param).__name__
61+
if hasattr(self, 'from_' + typename):
62+
return getattr(self, 'from_' + typename)(param)
63+
elif isinstance(param, ctypes.Array):
64+
return param
65+
else:
66+
raise TypeError("Can't convert %s" % typename)
67+
68+
# Cast from array.array objects
69+
def from_array(self, param):
70+
if param.typecode != 'd':
71+
raise TypeError('must be an array of doubles')
72+
ptr, _ = param.buffer_info()
73+
return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_double))
74+
75+
# Cast from lists/tuples
76+
def from_list(self, param):
77+
val = ((ctypes.c_double)*len(param))(*param)
78+
return val
79+
80+
from_tuple = from_list
81+
82+
# Cast from a numpy array
83+
def from_ndarray(self, param):
84+
return param.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
85+
86+
DoubleArray = DoubleArrayType()
87+
_avg = _mod.avg
88+
_avg.argtypes = (DoubleArray, ctypes.c_int)
89+
_avg.restype = ctypes.c_double
90+
91+
def avg(values):
92+
return _avg(values, len(values))
93+
94+
# struct Point { }
95+
class Point(ctypes.Structure):
96+
_fields_ = [('x', ctypes.c_double),
97+
('y', ctypes.c_double)]
98+
99+
# double distance(Point *, Point *)
100+
distance = _mod.distance
101+
distance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point))
102+
distance.restype = ctypes.c_double
104103
105104
If all goes well, you should be able to load the module and use the resulting C functions.
106105
For example:
107-
108-
>>> import sample
109-
>>> sample.gcd(35,42)
110-
7
111-
>>> sample.in_mandel(0,0,500)
112-
1
113-
>>> sample.in_mandel(2.0,1.0,500)
114-
0
115-
>>> sample.divide(42,8)
116-
(5, 2)
117-
>>> sample.avg([1,2,3])
118-
2.0
119-
>>> p1 = sample.Point(1,2)
120-
>>> p2 = sample.Point(4,5)
121-
>>> sample.distance(p1,p2)
122-
4.242640687119285
123-
>>>
106+
如果一切正常,你就可以加载并使用里面定义的C函数了。例如:
107+
108+
::
109+
110+
>>> import sample
111+
>>> sample.gcd(35,42)
112+
7
113+
>>> sample.in_mandel(0,0,500)
114+
1
115+
>>> sample.in_mandel(2.0,1.0,500)
116+
0
117+
>>> sample.divide(42,8)
118+
(5, 2)
119+
>>> sample.avg([1,2,3])
120+
2.0
121+
>>> p1 = sample.Point(1,2)
122+
>>> p2 = sample.Point(4,5)
123+
>>> sample.distance(p1,p2)
124+
4.242640687119285
125+
>>>
124126

125127
|
126128
127129
----------
128130
讨论
129131
----------
130-
There are several aspects of this recipe that warrant some discussion. The first issue
131-
concerns the overall packaging of C and Python code together. If you are using ctypes
132-
to access C code that you have compiled yourself, you will need to make sure that the
133-
shared library gets placed in a location where the sample.py module can find it. One
134-
possibility is to put the resulting .so file in the same directory as the supporting Python
135-
code. This is what’s shown at the first part of this recipe—sample.py looks at the __file__
136-
variable to see where it has been installed, and then constructs a path that points to a
137-
libsample.so file in the same directory.
138-
If the C library is going to be installed elsewhere, then you’ll have to adjust the path
139-
accordingly. If the C library is installed as a standard library on your machine, you might
140-
be able to use the ctypes.util.find_library() function. For example:
141-
142-
>>> from ctypes.util import find_library
143-
>>> find_library('m')
144-
'/usr/lib/libm.dylib'
145-
>>> find_library('pthread')
146-
'/usr/lib/libpthread.dylib'
147-
>>> find_library('sample')
148-
'/usr/local/lib/libsample.so'
149-
>>>
132+
本小节有很多值得我们详细讨论的地方。
133+
首先是对于C和Python代码一起打包的问题,如果你在使用 ``ctypes`` 来访问编译后的C代码,
134+
那么需要确保这个共享库放在 ``sample.py`` 模块同一个地方。
135+
一种可能是将生成的 ``.so`` 文件放置在要使用它的Python代码同一个目录下。
136+
我们在 ``recipe—sample.py`` 中使用 ``__file__`` 变量来查看它被安装的位置,
137+
然后构造一个指向同一个目录中的 ``libsample.so`` 文件的路径。
138+
139+
如果C函数库被安装到其他地方,那么你就要修改相应的路径。
140+
如果C函数库在你机器上被安装为一个标准库了,
141+
那么可以使用 ``ctypes.util.find_library()`` 函数来查找:
142+
143+
::
144+
145+
>>> from ctypes.util import find_library
146+
>>> find_library('m')
147+
'/usr/lib/libm.dylib'
148+
>>> find_library('pthread')
149+
'/usr/lib/libpthread.dylib'
150+
>>> find_library('sample')
151+
'/usr/local/lib/libsample.so'
152+
>>>
153+
154+
一旦你知道了C函数库的位置,那么就可以像下面这样使用 ``ctypes.cdll.LoadLibrary()`` 来加载它,
155+
其中 ``_path`` 是标准库的全路径:
150156

151-
Again, ctypes won’t work at all if it can’t locate the library with the C code. Thus, you’ll
152-
need to spend a few minutes thinking about how you want to install things.
153-
Once you know where the C library is located, you use ctypes.cdll.LoadLibrary()
154-
to load it. The following statement in the solution does this where _path is the full
155-
pathname to the shared library:
156157

157158
_mod = ctypes.cdll.LoadLibrary(_path)
158159

0 commit comments

Comments
 (0)