编写Cython扩展:如何从Python访问C结构内部数据? [英] Writing Cython extension: how to access a C struct internal data from Python?

查看:70
本文介绍了编写Cython扩展:如何从Python访问C结构内部数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

免责声明::我从 Python食谱(O'Reilly)中获得了以下示例。

Disclaimer: I took the following example from the Python Cookbook (O'Reilly).

说我有以下简单的 struct

typedef struct {
  double x,y;
} Point;

具有计算两个 Point s:

extern double distance(Point* p1, Point* p2);

所有这些都是名为 points


  • points.h -头文件

  • points.c -源文件

  • libpoints.so -库文件(Cython扩展链接到该文件)

  • points.h - the header file
  • points.c - the source file
  • libpoints.so - the library file (the Cython extension links against it)

我创建了环绕式Python脚本(称为 pypoints.py ):

I have created my wrapping Python script (called 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 和函数,我可以轻松地使用 ctypes cffi 但我想学习如何编写Cython扩展。在这里不需要 setup.py ,因此无需发布。

Note that this is just an example. For the struct and function above I can easily use ctypes or cffi but I want to learn how to write Cython extensions. The setup.py is not required here so no need to post it.

现在,您可以看到构造函数上面的允许我们做

Now as you can see the constructor above allows us to do

import points

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

dist = points.distance(p1, p2)

效果很好。但是,如果我想实际访问 Point 结构的内部怎么办?例如,我该怎么做

It works great. However what if I want to actually access the internals of the Point structure? For example how would I do

print("p1(x: " + str(p1.x) + ", y: " + str(p1.y))

您知道结构内部可以直接访问(如果使用C ++术语,我们可以说所有 struct 成员都是 public ),因此在C代码中我们可以轻松地完成

As you know a struct internals can be directly accessed (if we use C++ terminology we can say that all struct members are public) so in a C code we can easily do

Point p1 = {.x = 1., .y = 2.};
printf("p1(x: %f, y: %f)", p1.x, p1.y)

在Python类成员中( self.x self.y

In Python class members (self.x, self.y) can also be accessed without any getters and setters.

我可以编写充当中间步骤的函数:

I can write functions which act as an intermediate step:

double x(Point* p);
double y(Point* p);

但是我不确定如何包装它们以及如何在方法表中描述它们的调用。

however I am unsure how to wrap these and how to describe their call inside the table of methods.

我该怎么做?我想要一个简单的 p1.x 来获取我的 Po的 x Python中的int 结构。

How can I do that? I want to have a simple p1.x for getting the x of my Point structure in Python.

推荐答案

我最初对这个问题有点困惑,因为它似乎没有Cython内容(抱歉造成混乱的编辑混乱)。

I was initially a little confused about this question since it seemed to have no Cython content (sorry the editing mess resulting from that confusion).

Python食谱以非常奇怪的方式使用Cython ,我不建议您这样做。出于某种原因,它想使用在Cython中从未见过的PyCapsules。

The Python cookbook uses Cython in a very odd way which I wouldn't recommend following. For some reason it wants to use PyCapsules which I have never seen used before in Cython.

# 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编译并使用它了

You can then compile it and use it from Python as

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



<公平起见,它接着给出了第二个示例,该示例的行为与我所做的非常相似(但是使用的是Cython不支持类似Python的方法时使用的属性语法稍旧)。因此,如果您进一步阅读,则不需要此问题。但是请避免将Cython和pycapsules混合使用-这不是明智的解决方案,我也不知道他们为什么推荐使用它。

In fairness to the Python Cookbook it then gives a second example that does something very similar to what I've done (but using a slightly older property syntax from when Cython didn't support the Python-like approach). So if you'd have read a bit further you wouldn't have needed this question. But avoid mixing Cython and pycapsules - it isn't a sensible solution and I don't know why they recommended it.

这篇关于编写Cython扩展:如何从Python访问C结构内部数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆