2012-04-24 69 views
11

我有2類,A和B B,從A.用Cython和C++的繼承

//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接口這兩個類和必須調用木屐的可能性()方法繼承從A B實例:

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

雖然我希望這個代碼爲n不要做任何太危險的事情,我也希望有更好的辦法來處理它。

而且使用模塊生成以下的輸出:

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

而且我真的不喜歡分配的一個實例只是爲了後立即釋放它。

有關如何正確執行此操作的任何建議?

回答

7

我做了一些實驗,並有相當現成的答案,但現在我看到的問題是:

如果擴展類型都有一個基本類型,那麼 基本類型的__cinit__方法是自動調用之前你的__cinit__方法是 叫;您不能顯式調用繼承的__cinit__方法。

所以,真正的問題是,用Cython類型仍然不具備構造函數,只有預先初始化鉤__cinit__其行爲更像默認的構造函數。你不能從構造函數調用虛擬方法,也不能從__cinit__調用它(如果你打電話,它的行爲就像非虛擬)。

不知何故__cinit__type(self)返回正確的類型對象,但它是無用的。 Cython沒有靜態字段,方法和類型對象只能是type(沒有元類)的實例。 Python @staticmethod很容易覆蓋,所以它沒用。

因此,沒有其他方式喜歡將分配放入def __init__(self):,並檢查初始化的thisptr,無論您使用它。

您可能會考慮創建一個全局虛擬C++對象,並將其分配給thisptr以避免檢查和崩潰。沒有post初始化器掛鉤,所以你將無法檢查是否已經發生了正確的初始化。

3

我從來沒有注意過Cython之前,所以請原諒我,如果這是離開。這就是說:

在C++中,你有bar : foobarfoo繼承),如果foo有一個默認的構造函數,它會自動創建bar時調用任何時候....除非你打電話給自己的自定義構造函數父母。

我不知道Python,但一些快速谷歌搜索告訴我,相同的原則適用。即如果PyB不手動調用另一個,則只會調用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") 

請注意,我確定它很粗糙。但也許這裏的想法會給你一些工作?


這是另一個想法。我幾乎肯定它會工作(但語法是關閉的),它肯定比上面更清潔:

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++文檔makes it rather clear,有可能不是所有的C++改編慣用的解決方案,以模糊的,手工波浪引號,如「這可能需要通過其他一些嘗試(你呢?)找到的最優雅的方式處理這個問題。「

+0

好吧,通過打開賞金,我正在尋找一種慣用的構造對於這種情況,你說你不知道Python和Cython,雖然你的答案可以修改爲合法的Python(和Cython)代碼,但這會給Python用戶造成翻譯崩潰的威力,這是我的觀點這比浪費內存分配要糟糕多了 – ascobol 2012-05-09 17:30:22

+0

我的回覆太長了,請留言我的帖子的後半部分 – 2012-05-09 17:37:01

+0

a)我認爲在Cython中只能有一個構造函數,就像在Python中一樣 b)then在每種方法中,我們需要檢查一個正確的初始化... c)可能這是C模塊中任意預定義值的可能。在PyA __ cinit__中,我們將檢查一個額外的參數,這個非平凡的值可以繞過分配。在這種情況下,用戶不能「意外」地使譯員崩潰。 – ascobol 2012-05-09 18:51:46

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知識普遍缺乏......