Cython与C++继承

2024-09-28 23:15:54 发布

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

我有两个类,A和B。B继承自A

//C++    
class A
{
    public:
        int getA() {return this->a;};
        A() {this->a = 42;}
    private:
        int a;

};

class B: public A
{
    public:
       B() {this->b = 111;};
       int getB() {return this->b;};
    private:
        int b;

};

现在,我想使用Cython接口这两个类,并可以从B实例调用getA()方法:

^{pr2}$

当前我的pyx文件如下所示:

cdef extern from "Inherit.h" :
    cdef cppclass A:
       int getA()

    cdef cppclass B(A):
       int getB()


cdef class PyA:
    cdef A* thisptr

    def __cinit__(self):
       print "in A: allocating thisptr"
       self.thisptr = new A()
    def __dealloc__(self):
       if self.thisptr:
           print "in A: deallocating thisptr"
           del self.thisptr

    def getA(self):
       return self.thisptr.getA()

cdef class PyB(PyA):
    def __cinit__(self):
       if self.thisptr:
          print "in B: deallocating old A"
          del self.thisptr
       print "in B: creating new b"
       self.thisptr = new B()

    def __dealloc__(self):
       if self.thisptr:
           print "in B: deallocating thisptr"
           del self.thisptr
           self.thisptr = <A*>0

    def getB(self):
       return (<B*>self.thisptr).getB()

虽然我希望这段代码没有做任何太危险的事情,但我也希望有更好的方法来处理它。在

同时使用该模块生成以下输出:

>>> from inherit import *
>>> b = PyB()
in A: allocating thisptr
in B: deallocating old A
in B: creating new b
>>> b.getA()
42
>>> b.getB()
111
>>> del b
in B: deallocating thisptr

我也不喜欢分配一个实例来释放它。在

关于如何正确地做这件事有什么建议吗?在


Tags: inselfnewreturndefthisclassint
3条回答

我做了一些实验,有了很好的答案,但现在我知道问题出在哪里了:

If your extension type has a base type, the __cinit__ method of the base type is automatically called before your __cinit__ method is called; you cannot explicitly call the inherited __cinit__ method.

所以真正的问题是Cython类型仍然没有构造函数,只有pre-initializer hook __cinit__,它们的行为更像默认构造函数。不能从构造函数调用虚方法,也不能从__cinit__调用它(如果进行调用,它的行为类似于非虚方法)。在

不知何故在__cinit__内,type(self)返回了正确的类型对象,但它是无用的。Cython没有静态字段,方法和类型object只能是type的实例(没有元类)。{cd6>Python很容易被重写。在

因此,没有其他方法可以将分配放在def __init__(self):内,并在使用它的任何地方检查初始化的thisptr。在

您可以考虑创建全局虚拟C++对象,并将其赋值到{{CD8}},以避免检查和崩溃。没有post初始化器钩子,因此您将无法检查是否已经进行了正确的初始化。在

(我对Python和Cython都是新手,所以这个答案很有价值。)如果您在__init__函数中初始化thisptr,而不是在__cinit__函数中初始化thisptr,那么在这个特定的示例中,似乎没有额外的分配/删除。。。基本上将上面的__cinit__函数改为:

def __init__(self):
    print "in A: creating new A"
    self.thisptr = new A()

以及

^{pr2}$

分别。然而,我确信这至少在理论上是不安全的(而且可能实际上也是不安全的),但是也许有人可以评论一下到底有多不安全。。。在

{1{我可能不知道在这个例子中,{我们不能在这个例子中创建一个父类,^但我不能忘记这个例子中的一个子类。。。在

我以前从来没见过赛顿,所以请原谅我,如果这太离谱了。上面说:

C++中,任何时候你有{{*CD1>}(^ {CD2>}继承从^ {CD3>}),如果^ {< CD3>}有默认构造函数,则当^ {CD2>}被创建时它将被自动调用。除非您为父级调用自己的自定义构造函数。在

我不懂Python,但一些快速的google搜索告诉我同样的原则也适用。i、 只有当PyA没有手动调用另一个时,PyA的默认构造函数才会被调用。在

那样的话,这样的事情不管用吗?在

cdef class PyA:
    cdef A* thisptr

    def __cinit__(self, bypasskey="")
       if bypasskey == "somesecret"
           print "in A: bypassing allocation"
       else
           print "in A: allocating thisptr"
           self.thisptr = new A()

...

cdef class PyB(PyA):
    def __cinit__(self):
       super( PyB, self ).__init__("somesecret")

请注意,我肯定这很粗糙。但也许这里的想法会给你一些工作上的帮助?在


还有一个主意。我几乎可以肯定它会起作用(但是语法是关闭的),而且它肯定比上面的要干净得多:

^{pr2}$

或者看起来像这样?在

cdef class PyA:
    cdef A* thisptr

    def __cinit__(self, t=A)
           self.thisptr = new t()

...

cdef class PyB(PyA):
    def __cinit__(self):
       super( PyB, self ).__init__(B)

我不是为了赏金而发这个帖子的(你也不是被迫把它分配给任何人的),我只是和你分享一些想法。在

我认为你可以/应该能够避免“撞坏翻译”

a)使第二个构造函数只对b可见(不知道这是否可能),或

b)在其他地方使用前检查a是否为空,或

c)在绕过分配之前,检查调用函数是否是b的构造函数。在

也可以使用Cython C++文档{a1},可能没有所有C++解决方案的惯用解决方案,模糊的、挥手的引语如“它可能需要别人做一些实验(你?)找到最优雅的方法来处理这个问题。”

相关问题 更多 >