Cython和C ++继承

时间:2012-04-24 13:00:28

标签: c++ python inheritance cython

我有两个班,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()方法:

a = PyA()
b = PyB()
assert a.getA() == b.getA()

目前我的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

我真的不喜欢分配A实例只是为了立即释放它。

有关如何正确操作的任何建议吗?

3 个答案:

答案 0 :(得分:7)

我做了一些实验,并且已经准备好了答案,但现在我知道问题出在哪里了:

  

如果您的扩展程序类型具有基本类型,则使用__cinit__方法   在__cinit__方法之前自动调用基类型   所谓的;您无法显式调用继承的__cinit__方法。

所以真正的问题是Cython类型仍然没有构造函数,只有pre initializer hook __cinit__,它的行为更像默认构造函数。你不能从构造函数中调用虚方法,也不能从__cinit__调用它(如果你打电话,它的行为就像非虚拟的那样)。

__cinit__内部,type(self)返回正确的类型对象,但它没用。 Cython没有静态字段,方法和类型对象只能是type的实例(没有元类)。 Python @staticmethod很容易覆盖,所以没用。

所以没有其他方法可以将分配放在def __init__(self):中,并在任何地方检查初始化thisptr

您可以考虑创建一个全局虚拟C ++对象,并将其分配给thisptr以避免检查和崩溃。没有post initializer hook,因此你将无法检查是否已经进行了正确的初始化。

答案 1 :(得分:3)

我之前从未关注过Cython,所以请原谅我,如果这样的话。那说:

在C ++中,只要bar : foobar继承自foo),如果foo有默认构造函数,则{{1}时会自动调用它已创建....除非您为父级调用自己的自定义构造函数。

我不懂Python,但是一些快速的Google搜索告诉我相同的原则适用。即bar的默认构造函数仅在PyA未手动调用另一个时才会被调用。

在这种情况下,不会有这样的工作吗?

PyB

请注意,我确定这很粗糙。但也许这里的想法会给你一些合作的东西吗?


这是另一个想法。我几乎可以肯定它会起作用(但是语法已关闭),它肯定比上面的更清晰:

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")

或许它看起来像这样?

cdef class PyA:
    cdef A* thisptr

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

...

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

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

我认为如果你是

,你可以/应该能够避免“崩解翻译”

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

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

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

此外,Cython C ++文档makes it rather clear可能没有适用于所有C ++改编的​​惯用解决方案,含有模糊的手工波形引语,例如“可能需要其他人(你?)进行一些实验才能找到最多处理这个问题的优雅方法。“

答案 2 :(得分:1)

(我是Python和Cython的新手,所以请回答它的价值。)如果你在__init__函数而不是__cinit__函数中初始化thisptr,那么事情似乎在起作用这个特殊的例子没有额外的分配/删除...基本上将你的__cinit__函数更改为:

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

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

分别。但是,我确信这至少在理论上是不安全的(并且可能实际上也是不安全的),但也许有人可以评论究竟有多安全......

例如,从Cython介绍paper我们知道“__init__不能保证运行(例如,可以创建一个子类而忘记调用祖先构造函数)。 “我无法构建出现这种情况的测试用例,但这可能是由于我普遍缺乏Python知识......