我有两个班,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实例只是为了立即释放它。
有关如何正确操作的任何建议吗?
答案 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 : foo
(bar
继承自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知识......