【问题标题】:Return a struct from C to Python using Cython使用 Cython 将结构从 C 返回到 Python
【发布时间】:2018-08-10 19:43:41
【问题描述】:

我正在尝试将一个结构从一个 c 文件传回我的 Python。假设我有一个像这样的文件 pointc.c:

typedef struct Point {
    int x;
    int y;
} Point;

struct Point make_and_send_point(int x, int y);

struct Point make_and_send_point(int x, int y) {
    struct Point p = {x, y};
    return p;
}

然后我像这样设置一个 point.pyx 文件:

"# distutils: language = c"
# distutils: sources = pointc.c

cdef struct Point:
    int x
    int y

cdef extern from "pointc.c":
    Point make_and_send_point(int x, int y)

def make_point(int x, int y):
    return make_and_send_point(x, y) // This won't work, but compiles without the 'return' in-front of the function call

如何将返回的结构放入我的 Python 中?这种事情只能通过在 Cython 中创建一个结构并通过引用 void c 函数来发送吗?

作为参考,我的 setup.py 是:

from distutils.core import setup, Extension
from Cython.Build import cythonize

setup(ext_modules = cythonize(
      "point.pyx",
      language="c"
     )
)

【问题讨论】:

  • 这不会是一个有用的评论,但我从来没有真正理解为什么 cython 存在。用 c 编写 python 模块比使用 cython 更容易且更好。
  • 很公平,你有什么建议我直接写python模块的资源吗?我不熟悉将 c 与 python 连接起来。据我了解,Cython 有一些强大的工具,尽管使用起来有些成本。感谢您的评论!
  • 是的,python官方文档。只需 google writing python modules in c 并选择您要定位的版本。这实际上是关于python的一个痛苦,2和3版本的不兼容很糟糕。
  • 对比意见 - 如果您要与 python 对象进行大量交互,我认为 cython 更方便 - 没有新的 API 可以学习和引用计数簿记,这可能很微妙对,被照顾了。

标签: python c struct cython


【解决方案1】:

大多数情况下,您会编写某种包含 c 级结构的包装类,例如:

# point.pyx
cdef extern from "pointc.c":
    ctypedef struct Point:
        int x
        int y
    Point make_and_send_point(int x, int y)

cdef class PyPoint:
    cdef Point p

    def __init__(self, x, y):
        self.p = make_and_send_point(x, y)

    @property
    def x(self):
       return self.p.x

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

使用中

>>> import point
>>> p = point.PyPoint(10, 10)
>>> p.x
10

【讨论】:

  • 谢谢,我的做法略有不同,有点像@DavidW 通过获取返回的字典所指出的。但我喜欢这个答案,它显示了我实际上如何在 c 和 python 之间实现类,比我在网上找到的要清楚一些。谢谢。
【解决方案2】:

Cython 给定结构的默认行为是将其转换为 Python 字典,这对您来说可能已经足够了。 (但这仅适用于由简单类型组成的结构)。

这不起作用有几个原因。首先,您应该从headers 中执行cdef extern from,而不是源文件,否则您会收到有关多个定义的错误(我认为这只是创建最小示例时的错误)。其次,您需要将 Point 的定义放在您的 cdef extern 块中:

cdef extern from "pointc.h":
    cdef struct Point:
        int x
        int y

如果您不这样做,那么 Cython 会为您的结构 (__pyx_t_5point_Point) 创建一个不匹配的内部名称,该名称与 C 函数签名不匹配,因此会失败。

修正此问题后,您将获得将结构体转换为字典的正确默认行为。 (这应该双向工作 - 您可以将字典转换回结构)。如果这不是您想要的,请遵循@chrisb 的回答

【讨论】:

  • 谢谢,是的,这就是我最初所做的。我制作了 Cython 结构,然后通过引用 c 中的 void make_and_send 函数将其传递。
  • 顺便说一句,c 文件相当简单,所以我将所有函数声明都放在顶部,并且从“pointc.c”执行 cdef extern 不会收到任何警告。是否有文档说我应该使用标题以及为什么?
  • 在 C 语言中,cdef extern from ... 块包含文件。作为 C 语言中的一条规则,您应该只包含标头以避免拥有相同代码的多个副本。这是您应该从 C 的角度而不是 Cython 的角度来看的东西。根据编译器的不同,您可能不会收到错误/警告,因此如果它可以正常工作,那很好,但它可能不是最佳实践。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-26
  • 1970-01-01
相关资源
最近更新 更多