@@ -208,135 +208,137 @@ Cython会生成包装代码来正确的转换参数和返回值。
208
208
raise ValueError("y must be > 0")
209
209
return csample.gcd(x,y)
210
210
211
- The declaration of in_mandel() in the csample.pxd file has an interesting, but subtle
212
- definition. In that file, the function is declared as returning a bint instead of an int.
213
- This causes the function to create a proper Boolean value from the result instead of a
214
- simple integer. So, a return value of 0 gets mapped to False and 1 to True.
215
- ``in_mandel() `` 的声明
216
-
217
- Within the Cython wrappers, you have the option of declaring C data types in addition
218
- to using all of the usual Python objects. The wrapper for divide() shows an example
219
- of this as well as how to handle a pointer argument.
220
-
221
- def divide(x,y):
222
- cdef int rem
223
- quot = csample.divide(x,y,&rem)
224
- return quot, rem
225
-
226
- Here, the rem variable is explicitly declared as a C int variable. When passed to the
227
- underlying divide() function, &rem makes a pointer to it just as in C.
228
- The code for the avg() function illustrates some more advanced features of Cython.
229
- First the declaration def avg(double[:] a) declares avg() as taking a one-dimensional
230
- memoryview of double values. The amazing part about this is that the resulting function
231
- will accept any compatible array object, including those created by libraries such as
232
- numpy. For example:
233
- >>> import array
234
- >>> a = array.array(' d' ,[1 ,2 ,3 ])
235
- >>> import numpy
236
- >>> b = numpy.array([1 ., 2 ., 3 .])
237
- >>> import sample
238
- >>> sample.avg(a)
239
- 2.0
240
- >>> sample.avg(b)
241
- 2.0
242
- >>>
243
-
244
- In the wrapper, a.size and &a[0] refer to the number of array items and underlying
245
- pointer, respectively. The syntax <double *> &a[0] is how you type cast pointers to a
246
- different type if necessary. This is needed to make sure the C avg() receives a pointer
247
- of the correct type. Refer to the next recipe for some more advanced usage of Cython
248
- memoryviews.
249
- In addition to working with general arrays, the avg() example also shows how to work
250
- with the global interpreter lock. The statement with nogil: declares a block of code as
251
- executing without the GIL. Inside this block, it is illegal to work with any kind of normal
252
- Python object—only objects and functions declared as cdef can be used. In addition to
253
- that, external functions must explicitly declare that they can execute without the GIL.
254
- Thus, in the csample.pxd file, the avg() is declared as double avg(double *, int)
255
- nogil.
256
- The handling of the Point structure presents a special challenge. As shown, this recipe
257
- treats Point objects as opaque pointers using capsule objects, as described in
258
- Recipe 15.4. However, to do this, the underlying Cython code is a bit more complicated.
259
- First, the following imports are being used to bring in definitions of functions from the
260
- C library and Python C API:
261
-
262
- from cpython.pycapsule cimport *
263
- from libc.stdlib cimport malloc, free
264
-
265
- The function del_Point() and Point() use this functionality to create a capsule object
266
- that wraps around a Point * pointer. The declaration cdef del_Point() declares
267
- del_Point() as a function that is only accessible from Cython and not Python. Thus,
268
- this function will not be visible to the outside—instead, it’s used as a callback function
269
- to clean up memory allocated by the capsule. Calls to functions such as PyCap
270
- sule_New(), PyCapsule_GetPointer() are directly from the Python C API and are used
271
- in the same way.
272
- The distance() function has been written to extract pointers from the capsule objects
273
- created by Point(). One notable thing here is that you simply don’t have to worry about
274
- exception handling. If a bad object is passed, PyCapsule_GetPointer() raises an ex‐
275
- ception, but Cython already knows to look for it and propagate it out of the dis
276
- tance() function if it occurs.
277
- A downside to the handling of Point structures is that they will be completely opaque
278
- in this implementation. You won’t be able to peek inside or access any of their attributes.
279
- There is an alternative approach to wrapping, which is to define an extension type, as
280
- shown in this code:
281
-
282
- # sample.pyx
283
-
284
- cimport csample
285
- from libc.stdlib cimport malloc, free
286
- ...
287
-
288
- cdef class Point:
289
- cdef csample.Point *_c_point
290
- def __cinit__(self, double x, double y):
291
- self._c_point = <csample.Point *> malloc(sizeof(csample.Point))
292
- self._c_point.x = x
293
- self._c_point.y = y
294
-
295
- def __dealloc__(self):
296
- free(self._c_point)
297
-
298
- property x:
299
- def __get__(self):
300
- return self._c_point.x
301
- def __set__(self, value):
302
- self._c_point.x = value
303
-
304
- property y:
305
- def __get__(self):
306
- return self._c_point.y
307
- def __set__(self, value):
308
- self._c_point.y = value
309
-
310
- def distance(Point p1, Point p2):
311
- return csample.distance(p1._c_point, p2._c_point)
312
-
313
- Here, the cdef class Point is declaring Point as an extension type. The class variable
314
- cdef csample.Point *_c_point is declaring an instance variable that holds a pointer
315
- to an underlying Point structure in C. The __cinit__() and __dealloc__() methods
316
- create and destroy the underlying C structure using malloc() and free() calls. The
317
- property x and property y declarations give code that gets and sets the underlying
318
- structure attributes. The wrapper for distance() has also been suitably modified to
319
- accept instances of the Point extension type as arguments, but pass the underlying
320
- pointer to the C function.
321
- Making this change, you will find that the code for manipulating Point objects is more
322
- natural:
323
-
324
- >>> import sample
325
- >>> p1 = sample.Point(2 ,3 )
326
- >>> p2 = sample.Point(4 ,5 )
327
- >>> p1
328
- <sample.Point object at 0x100447288>
329
- >>> p2
330
- <sample.Point object at 0x1004472a0>
331
- >>> p1.x
332
- 2.0
333
- >>> p1.y
334
- 3.0
335
- >>> sample.distance(p1,p2)
336
- 2.8284271247461903
337
- >>>
338
-
339
- This recipe has illustrated many of Cython’s core features that you might be able to
340
- extrapolate to more complicated kinds of wrapping. However, you will definitely want
341
- to read more of the official documentation to do more.
342
- The next few recipes also illustrate a few additional Cython features.
211
+ 在csample.pxd文件中的``in_mandel()`` 声明有个很有趣但是比较难理解的定义。
212
+ 在这个文件中,函数被声明为然后一个bint而不是一个int。
213
+ 它会让函数创建一个正确的Boolean值而不是简单的整数。
214
+ 因此,返回值0表示False而1表示True。
215
+
216
+ 在Cython包装器中,你可以选择声明C数据类型,也可以使用所有的常见Python对象。
217
+ 对于 ``divide() `` 的包装器展示了这样一个例子,同时还有如何去处理一个指针参数。
218
+
219
+ ::
220
+
221
+ def divide(x,y):
222
+ cdef int rem
223
+ quot = csample.divide(x,y,&rem)
224
+ return quot, rem
225
+
226
+ 在这里,``rem `` 变量被显示的声明为一个C整型变量。
227
+ 当它被传入 ``divide() `` 函数的时候,``&rem `` 创建一个跟C一样的指向它的指针。
228
+ ``avg() `` 函数的代码演示了Cython更高级的特性。
229
+ 首先 ``def avg(double[:] a) `` 声明了 ``avg() `` 接受一个一维的双精度内存视图。
230
+ 最惊奇的部分是返回的结果函数可以接受任何兼容的数组对象,包括被numpy创建的。例如:
231
+
232
+ ::
233
+
234
+ >>> import array
235
+ >>> a = array.array('d',[1,2,3])
236
+ >>> import numpy
237
+ >>> b = numpy.array([1., 2., 3.])
238
+ >>> import sample
239
+ >>> sample.avg(a)
240
+ 2.0
241
+ >>> sample.avg(b)
242
+ 2.0
243
+ >>>
244
+
245
+ 在此包装器中,``a.size0 `` 和 ``&a[0] `` 分别引用数组元素个数和底层指针。
246
+ 语法 ``<double *> &a[0] `` 教你怎样将指针转换为不同的类型。
247
+ 前提是C中的 ``avg() `` 接受一个正确类型的指针。
248
+ 参考下一节关于Cython内存视图的更高级讲述。
249
+
250
+ 除了处理通常的数组外,``avg() `` 的这个例子还展示了如何处理全局解释器锁。
251
+ 语句 ``with nogil: `` 声明了一个不需要GIL就能执行的代码块。
252
+ 在这个块中,不能有任何的普通Python对象——只能使用被声明为 ``cdef `` 的对象和函数。
253
+ 另外,外部函数必须现实的声明它们能不依赖GIL就能执行。
254
+ 因此,在csample.pxd文件中,``avg() `` 被声明为 ``double avg(double *, int) nogil `` .
255
+
256
+ 对Point结构体的处理是一个挑战。本节使用胶囊对象将Point对象当做隐形指针来处理,这个在15.4小节介绍过。
257
+ 要这样做的话,底层Cython代码稍微有点复杂。
258
+ 首先,下面的导入被用来引入C函数库和Python C API中定义的函数:
259
+
260
+ ::
261
+
262
+ from cpython.pycapsule cimport *
263
+ from libc.stdlib cimport malloc, free
264
+
265
+ 函数 ``del_Point() `` 和 ``Point() `` 使用这个功能来创建一个胶囊对象,
266
+ 它会包装一个 ``Point * `` 指针。``cdef del_Point() `` 将 ``del_Point() `` 声明为一个函数,
267
+ 只能通过Cython访问,而不能从Python中访问。
268
+ 因此,这个函数对外部是不可见的——它被用来当做一个回调函数来清理胶囊分配的内存。
269
+ 函数调用比如 ``PyCapsule_New() `` 、``PyCapsule_GetPointer() ``
270
+ 直接来自Python C API并且以同样的方式被使用。
271
+
272
+ ``distance `` 函数从 ``Point() `` 创建的胶囊对象中提取指针。
273
+ 这里要注意的是你不需要担心异常处理。
274
+ 如果一个错误的对象被传进来,``PyCapsule_GetPointer() `` 会抛出一个异常,
275
+ 但是Cython已经知道怎么查找到它,并将它从 ``distance() `` 传递出去。
276
+
277
+ 处理Point结构体一个缺点是它的实现是不可见的。
278
+ 你不能访问任何属性来查看它的内部。
279
+ 这里有另外一种方法去包装它,就是定义一个扩展类型,如下所示:
280
+
281
+ ::
282
+
283
+ # sample.pyx
284
+
285
+ cimport csample
286
+ from libc.stdlib cimport malloc, free
287
+ ...
288
+
289
+ cdef class Point:
290
+ cdef csample.Point *_c_point
291
+ def __cinit__(self, double x, double y):
292
+ self._c_point = <csample.Point *> malloc(sizeof(csample.Point))
293
+ self._c_point.x = x
294
+ self._c_point.y = y
295
+
296
+ def __dealloc__(self):
297
+ free(self._c_point)
298
+
299
+ property x:
300
+ def __get__(self):
301
+ return self._c_point.x
302
+ def __set__(self, value):
303
+ self._c_point.x = value
304
+
305
+ property y:
306
+ def __get__(self):
307
+ return self._c_point.y
308
+ def __set__(self, value):
309
+ self._c_point.y = value
310
+
311
+ def distance(Point p1, Point p2):
312
+ return csample.distance(p1._c_point, p2._c_point)
313
+
314
+ 在这里,cdif类 ``Point `` 将Point声明为一个扩展类型。
315
+ 类属性 ``cdef csample.Point *_c_point `` 声明了一个实例变量,
316
+ 拥有一个指向底层Point结构体的指针。
317
+ ``__cinit__() `` 和 ``__dealloc__() `` 方法通过 ``malloc() `` 和 ``free() `` 创建并销毁底层C结构体。
318
+ x和y属性的声明让你获取和设置底层结构体的属性值。
319
+ ``distance() `` 的包装器还可以被修改,使得它能接受 ``Point `` 扩展类型实例作为参数,
320
+ 而传递底层指针给C函数。
321
+
322
+ 做了这个改变后,你会发现操作Point对象就显得更加自然了:
323
+
324
+ ::
325
+
326
+ >>> import sample
327
+ >>> p1 = sample.Point(2,3)
328
+ >>> p2 = sample.Point(4,5)
329
+ >>> p1
330
+ <sample.Point object at 0x100447288>
331
+ >>> p2
332
+ <sample.Point object at 0x1004472a0>
333
+ >>> p1.x
334
+ 2.0
335
+ >>> p1.y
336
+ 3.0
337
+ >>> sample.distance(p1,p2)
338
+ 2.8284271247461903
339
+ >>>
340
+
341
+ 本节已经演示了很多Cython的核心特性,你可以以此为基准来构建更多更高级的包装。
342
+ 不过,你最好先去阅读下官方文档来了解更多信息。
343
+
344
+ 接下来几节还会继续演示一些Cython的其他特性。
0 commit comments