免责声明:我从Python食谱(O'Reilly)中获取了以下示例。
假设我有以下简单的struct

typedef struct {
  double x,y;
} Point;

使用计算两个Points之间欧几里德距离的函数:
extern double distance(Point* p1, Point* p2);

所有这些都是名为points的共享库的一部分:
points.h-头文件
points.c-源文件
libpoints.so-库文件(Cython扩展名链接到它)
我已经创建了包装Python脚本(称为pypoints.py):
#include "Python.h"
#include "points.h"


// Destructor for a Point instance
static void del_Point(PyObject* obj) {
  // ...
}

// Constructor for a Point instance
static void py_Point(PyObject* obj) {
  // ...
}

// Wrapper for the distance function
static PyObject* py_distance(PyObject* self, PyObject* arg) {
  // ...
}

// Method table
static PyMethodDef PointsMethods[] = {
  {"Point", py_Point, METH_VARARGS, "Constructor for a Point"},
  {"distance", py_distance, METH_VARARGS, "Calculate Euclidean distance between two Points"}
}

// Module description
static struct PyModuleDef pointsmodule = {
  PyModuleDef_HEAD_INIT,
  "points", // Name of the module; use "import points" to use
  "A module for working with points", // Doc string for the module
  -1,
  PointsMethods // Methods provided by the module
}

注意这只是一个例子。对于上面的struct和函数,我可以很容易地使用ctypescffi,但我想学习如何编写Cython扩展。此处不需要setup.py,因此无需发布。
现在正如您所看到的,上面的构造函数允许我们
import points

p1 = points.Point(1, 2)   # Calls py_Point(...)
p2 = points.Point(-3, 7)  # Calls py_Point(...)

dist = points.distance(p1, p2)

效果很好。但是,如果我想实际访问Point结构的内部呢?比如我该怎么做
print("p1(x: " + str(p1.x) + ", y: " + str(p1.y))

如你所知,struct内部可以直接访问(如果使用C++术语,我们可以说所有struct成员都是public),所以在C代码中我们可以很容易地做。
Point p1 = {.x = 1., .y = 2.};
printf("p1(x: %f, y: %f)", p1.x, p1.y)

在Python类中,也可以在不使用任何getter和setter的情况下访问成员(self.xself.y)。
我可以编写作为中间步骤的函数:
double x(Point* p);
double y(Point* p);

但是,我不确定如何包装它们,以及如何在方法表中描述它们的调用。
我该怎么做?我想要一个简单的p1.x来获取Python中我的x结构的Point

最佳答案

我最初对这个问题有点困惑,因为它似乎没有Cython内容(抱歉,编辑混乱造成的混乱)。
我不建议遵循的Python cookbook uses Cython in a very odd way。出于某种原因,它想使用我以前从未见过在Cython中使用过的PyCapsules。

# tell Cython about what's in "points.h"
# (this does match the cookbook version)

cdef extern from "points.h"
    ctypedef struct Point:
        double x
        double y

    double distance(Point *, Point *)

# Then we describe a class that has a Point member "pt"
cdef class Py_Point:
    cdef Point pt

    def __init__(self,x,y):
        self.pt.x = x
        self.pt.y = y

    # define properties in the normal Python way
    @property
    def x(self):
        return self.pt.x

    @x.setter
    def x(self,val):
        self.pt.x = val

    @property
    def y(self):
        return self.pt.y

    @y.setter
    def y(self,val):
        self.pt.y = val

def py_distance(Py_Point a, Py_Point b):
    return distance(&a.pt,&b.pt) # get addresses of the Point members

然后,您可以编译它并使用Python中的
from whatever_your_module_is_called import *

# create a couple of points
pt1 = Py_Point(1.3,4.5)
pt2 = Py_Point(1.5,0)

print(pt1.x, pt1.y) # access the members

print(py_distance(pt1,pt2)) # distance between the two

为了公平起见,Python食谱给出了第二个例子,它所做的事情与我所做的非常相似(但是使用的是Cython不支持类似Python的方法时的稍旧的属性语法)。所以如果你读得再深入一点,你就不需要这个问题了。但是避免混合Cython和PyC囊——这不是一个明智的解决方案,我不知道他们为什么推荐它。

07-26 09:38
查看更多