如何优雅地将 C 指针返回给 Python
哈哈,想不到吧,Cython 系列居然诈尸了。关于 Cython 的语法我们已经介绍完了,接下来会介绍一些使用 Cython 时的小技巧以及一些遗漏的细节。
而本次介绍的是如何将一个 C 指针优雅地返回给 Python。
假设有一个 C 头文件 rect.h 和一个 C 源文件 rect.c,其中 rect.h 的内容如下:
typedef struct {
int x;
int y;
} Rectangle;Rectangle* create_rectangle(int, int);
rect.c 的内容如下:
#include "rect.h"
#include <stdlib.h>Rectangle* create_rectangle(int x, int y) {
Rectangle* r = (Rectangle*)malloc(sizeof(Rectangle));
r -> x = x;
r -> y = y;
return r;
}然后我们要基于 create_rectangle 这个 C 函数构建相应的结构体实例,拿到它的指针并返回给 Python,那么该怎么做呢?首先 Python 在语言层面没有指针的概念,所以如果想返回指针的话,那么只能将指针包上一层。
编写 Cython 源文件:
from libc.stdlib cimport freecdef
extern from "./rect.h":
# 我们暴露给 Python 的类也叫 Rectangle
# 因此为了避免冲突,这里起个别名
ctypedef struct_Rectangle "Rectangle":
int x
int y_Rectangle* create_rectangle (int x, int y);cdef class Rectangle:
"""
定义一个扩展类,用于包装结构体指针
"""
cdef
_Rectangle *rect def __init__(self,
int x, int y):
# 创建结构体实例,拿到它的指针保存起来
# 显然这个 rect 属性不能暴露给外界
self.rect = create_rectangle(x, y) # 提供专门的接口用于操作属性
property x:
def __get__(self):
return self.rect.x def __set__(self,
int x):
self.rect.x = xproperty y:
def __get__(self):
return self.rect.y def __set__(self,
int y):
self.rect.y = y def get_area(self):
"""返回矩形的面积"""
return self.x * self.y
def __dealloc__(self):
# 当实例被销毁时
# 释放堆区的 C 结构体内存
if self.rect != NULL:
free(<
void *> self.rect)
print("堆内存被释放")文件名叫 cython_test.pyx,我们编译一下,然后导入测试:
import cython_testrect = cython_test.Rectangle(10, 20)
print(rect.x, rect.y) # 10 20
print(rect.get_area()) # 200
rect.x = 100
rect.y = 200
print(rect.x, rect.y) # 100 200
print(rect.get_area()) # 20000
del rect
print("--------------")
"""
堆内存被释放
--------------
"""
从目前来看效果还是不错的,因为 Python 无法操作指针,因此直接返回指针是会报错的,只能在 Cython 内部操作。但可以自定义一个类,将指针隐藏在里面,然后再专门提供一些接口,供外界使用。外界调用相应的接口,然后再由 Cython 在内部操作,非常完美。