问题 Cython和C ++继承
我有2个班,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实例只是为了立即释放它。
关于如何正确地做到这一点的任何建议?
4009
2018-04-24 13:00
起源
答案:
我做了一些实验,并且已经准备好了答案,但现在我知道问题出在哪里:
如果您的扩展类型具有基本类型,则 __cinit__
的方法
在您之前自动调用基类型 __cinit__
方法是
所谓的;你不能显式调用继承 __cinit__
方法。
所以真正的问题是Cython类型仍然没有构造函数,只有pre initializer hook __cinit__
其行为更像默认构造函数。您无法从构造函数中调用虚方法,也无法从中调用它 __cinit__
要么(如果你打电话,它表现得像非虚拟)。
不知何故在里面 __cinit__
该 type(self)
返回正确的类型对象,但它没用。 Cython没有静态字段,方法和类型对象只能是实例 type
(没有元类)。蟒蛇 @staticmethod
容易覆盖,所以没用。
所以没有其他方法可以将分配放在里面 def __init__(self):
,并检查初始化 thisptr
无论你在哪里使用它。
您可以考虑创建一个全局虚拟C ++对象,并将其分配给 thisptr
避免检查和崩溃。没有post initializer hook,因此您将无法检查是否已经进行了正确的初始化。
7
2018-05-09 23:34
我之前从未看过Cython,所以请原谅我,如果这样的话。那说:
在C ++中,只要你有 bar : foo
(bar
继承自 foo
),如果 foo
有一个默认的构造函数,它会在自动调用时调用 bar
已创建....除非您为父级调用自己的自定义构造函数。
我不懂Python,但一些快速的谷歌搜索告诉我同样的原则适用。即默认构造函数 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))
或者它看起来像这样?
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 ++文档 使它相当清楚 可能没有惯用的解决方案可以解决所有C ++的改编,用模糊的,手工波浪的引用,例如“可能需要其他人(你?)进行一些试验才能找到处理这个问题的最优雅的方法。”
3
2018-05-09 16:18
答案:
我做了一些实验,并且已经准备好了答案,但现在我知道问题出在哪里:
如果您的扩展类型具有基本类型,则 __cinit__
的方法
在您之前自动调用基类型 __cinit__
方法是
所谓的;你不能显式调用继承 __cinit__
方法。
所以真正的问题是Cython类型仍然没有构造函数,只有pre initializer hook __cinit__
其行为更像默认构造函数。您无法从构造函数中调用虚方法,也无法从中调用它 __cinit__
要么(如果你打电话,它表现得像非虚拟)。
不知何故在里面 __cinit__
该 type(self)
返回正确的类型对象,但它没用。 Cython没有静态字段,方法和类型对象只能是实例 type
(没有元类)。蟒蛇 @staticmethod
容易覆盖,所以没用。
所以没有其他方法可以将分配放在里面 def __init__(self):
,并检查初始化 thisptr
无论你在哪里使用它。
您可以考虑创建一个全局虚拟C ++对象,并将其分配给 thisptr
避免检查和崩溃。没有post initializer hook,因此您将无法检查是否已经进行了正确的初始化。
7
2018-05-09 23:34
我之前从未看过Cython,所以请原谅我,如果这样的话。那说:
在C ++中,只要你有 bar : foo
(bar
继承自 foo
),如果 foo
有一个默认的构造函数,它会在自动调用时调用 bar
已创建....除非您为父级调用自己的自定义构造函数。
我不懂Python,但一些快速的谷歌搜索告诉我同样的原则适用。即默认构造函数 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))
或者它看起来像这样?
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 ++文档 使它相当清楚 可能没有惯用的解决方案可以解决所有C ++的改编,用模糊的,手工波浪的引用,例如“可能需要其他人(你?)进行一些试验才能找到处理这个问题的最优雅的方法。”
3
2018-05-09 16:18
(我是Python和Cython的新手,所以请将这个答案用于它的价值。)如果你初始化thisptr in __init__
功能而不是 __cinit__
功能,事情似乎在这个特定的例子中工作,没有额外的分配/删除...基本上改变你的 __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的介绍 纸 我们知道 ”__init__
不能保证运行(例如,可以创建一个子类而忘记调用祖先构造函数)。“我无法构建一个发生这种情况的测试用例,但这可能是由于普遍缺乏我的Python知识......
1
2018-05-23 20:59