返回函数pythoncapi

2024-04-16 08:08:01 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在为一个C库创建Python绑定。在

在C中,使用函数的代码如下所示:

Ihandle *foo;
foo = MethFunc();
SetArribute(foo, 's');

我正试图把这个放到Python中。其中,我有MethFunc()SetAttribute()函数,可以在Python代码中使用:

^{pr2}$

到目前为止,返回函数的C代码如下所示:

static PyObject * _MethFunc(PyObject *self, PyObject *args) {
    return Py_BuildValue("O", MethFunc());
}

但失败的是崩溃(没有错误)

我也尝试过return MethFunc();,但失败了。在

如何返回函数foo(或者如果我试图实现的功能完全错误,我应该如何将MethFunc()传递给SetAttribute())?在


Tags: 函数代码selfreturnfoo错误argsstatic
1条回答
网友
1楼 · 发布于 2024-04-16 08:08:01

这里的问题是MethFunc()返回一个IHandle *,但您告诉Python将其视为PyObject *。大概那些是完全不相关的类型。在

一个PyObject *(或您或Python定义的任何以适当的^{}宏开头的struct)以指向refcount和类型的指针开始,Python对您手上的任何对象要做的第一件事就是处理这些指针。因此,如果你给它一个以两个int开头的对象,Python最终将尝试访问位于0x00020001或类似位置的类型,这几乎肯定是segfault。在

如果需要传递指向某个C对象的指针,则必须将其包装在Python对象中。有三种方法可以做到这一点,从最粗糙到最坚实。在


首先,您可以将IHandle *转换为size_t,然后PyLong_FromSize_t它。在

这很容易实现。但这意味着从Python的角度来看,这些对象看起来和数字完全一样,因为这就是它们的全部。在

显然,您不能将方法附加到这个数字;相反,您的API必须是一个自由函数,它接受一个数字,然后将该数字转换回一个IHandle*并调用一个方法。在

它更像是,例如,C的stdio,在这里,您必须将stdin或{}作为参数传递给fread,而不是Python的io,在这里您调用sys.stdin或{}上的方法。在

但更糟糕的是,因为没有静态或动态的类型检查来防止某些Python代码意外地向您传递数字42。然后将其转换为IHandle *并尝试取消引用,从而导致一个segfault

如果您希望Python的垃圾回收器能够帮助您知道对象何时仍然被引用,那么您就走运了。您需要让您的用户手动跟踪该号码,并在完成后调用某个CloseHandle函数。在

实际上,这并没有比从ctypes访问代码好多少,所以希望这能激励您继续阅读。在


更好的解决方案是将IHandle *转换为void *,然后PyCapsule_New它。在

如果你没有读过capsules,你至少需要浏览一下主要章节。但其基本思想是将void*封装为Python对象。在

所以,这几乎和传递数字一样简单,但解决了大多数问题。胶囊是不透明的值,Python用户不会意外地对其进行算术运算;他们不能发送给您42来代替胶囊;您可以附加一个函数,该函数在对胶囊的最后一次引用消失时被调用;您甚至可以给它一个很好的名称来显示在repr中。在

但你还是不能把任何行为附加到胶囊上。在

因此,如果mymodule是一个胶囊,那么API仍然必须是MethSetAttribute(mymodule, foo),而不是{},就像它是一个int。(但现在它是类型安全的。)


最后,您可以为包含IHandle *的结构构建一个新的Python扩展类型。在

这是一个更多的工作。如果你还没读过关于Defining Extension Types的教程,你需要通读整个章节。在

但这意味着您有一个实际的Python类型,以及它所附带的一切。在

您可以给它一个SetAttribute方法,Python代码可以直接调用该方法。你可以给它任何你想要的__str__和{}。你可以给它一个__doc__。Python代码可以isinstance(mymodule, MyMeth)。等等。在


<>如果你愿意使用C++,或者D,或者锈,而不是C,那么有一些很棒的库(pycxx,Booo::python,pyd,锈蚀python等)可以为你做大部分的样板。您只需声明您想要一个Python类,以及如何将其属性和方法绑定到C属性和方法中,并且可以得到像C++类一样使用的东西,除了它实际上是一个覆盖在^ {CD3}}下的东西。(它甚至可以通过RA为您处理所有的重新计算二、 这将为您节省无休止的周末调试segfaults和内存泄漏…)

或者您可以使用Cython,它允许您用基本上是Python的语言编写C扩展模块,但是扩展到与C代码的接口。所以你的包装类只是一个class,但是有一个特殊的私有cdef属性,它包含{},你的{}可以用这个私有属性调用CSetAttribute函数。在

或者,根据用户的建议,您也可以使用SWIG为您生成C绑定。对于简单的情况,只需将您的C API提供给它,它就可以返回构建Python.so的代码。对于一些不太简单的案例,我个人觉得它比PyCxx这样痛苦得多,但是如果你还不知道C++,它肯定有更低的学习曲线。在

相关问题 更多 >