13

A と B の 2 つのクラスがあります。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 を使用してこれら 2 つのクラスをインターフェースし、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 インスタンスを割り当ててすぐに解放するのはあまり好きではありません。

正しく行う方法について何かアドバイスはありますか?

4

3 に答える 3

8

私はいくつかの実験を行い、かなり準備ができていますが、問題がどこにあるかがわかりました。

拡張タイプに基本タイプがある場合、メソッドが呼び出さ__cinit__れる前に、基本タイプのメソッドが自動的に呼び出さ__cinit__れます。__cinit__継承されたメソッドを明示的に呼び出すことはできません。

したがって、本当の問題は、Cython型にはまだコンストラクターがなく、__cinit__デフォルトのコンストラクターのように動作する初期化前のフックだけがあることです。コンストラクターから仮想メソッドを呼び出すことはできません。また、__cinit__どちらからも呼び出すことはできません(呼び出しを行うと、非仮想メソッドのように動作します)。

どういうわけか正しい型オブジェクトを返しますが__cinit__type(self)それは役に立ちません。Cythonには静的フィールドがなく、メソッドと型オブジェクトはのインスタンスのみになりますtype(メタクラスはありません)。Python@staticmethodは簡単に上書きできるので、役に立たない。

def __init__(self):したがって、割り当てを内部に配置し、使用する場所で初期化されているかどうかを確認する方法は他にありませんthisptr

グローバルダミーC++オブジェクトを作成し、それをに割り当てて、thisptrチェックやクラッシュを回避することを検討してください。ポストイニシャライザフックがないため、正しい初期化がすでに行われているかどうかを確認できません。

于 2012-05-09T23:34:41.410 に答える
3

私は 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) 2 番目のコンストラクターを b だけに見えるようにする (これが可能かどうかは不明)、または

b) 他の場所で使用する前に a が null かどうかを確認する、または

c) 割り当てをバイパスする前に、呼び出し関数が b のコンストラクターであったことを確認します。

また、Cython C++ のドキュメンテーションは、すべての C++ 適応に対する慣用的な解決策がない可能性があることをかなり明確にしています。この問題。"

于 2012-05-09T16:18:22.843 に答える
1

(私はPythonとCythonの両方に慣れていないので、この答えを価値のあるものにしてください。)__init__関数ではなく関数でthisptrを初期化する__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 に関する一般的な知識が不足していることが原因です...

于 2012-05-23T20:59:17.967 に答える