3

私はいくつかのctypesバインディングを持っており、bodyごとに新しいbody.Freeと呼ぶ必要があります。私がバインドしているライブラリには、残りのコードから分離された割り当てルーチンがありません(それらはどこからでも呼び出すことができます)。また、循環参照を作成するために必要ないくつかの便利な機能を使用します。

デストラクタをオブジェクトにフックする信頼できる方法を見つければ解決すると思います。(weakrefsは、データがドロップされる直前にコールバックを提供してくれる場合に役立ちます。

したがって、velocity_funcを入力すると、明らかにこのコードはメガフェイルになります。

class Body(object):
    def __init__(self, mass, inertia):
        self._body = body.New(mass, inertia)

    def __del__(self):
        print '__del__ %r' % self
        if body:
            body.Free(self._body)

    ...        

    def set_velocity_func(self, func):
        self._body.contents.velocity_func = ctypes_wrapping(func)

私はまた、weakrefsを介してそれを解決しようとしましたが、それらの状況は悪化しているように見えますが、ほとんど予測できません。

私がvelocity_funcを入力しなくても、これを実行すると、少なくともその時点でサイクルが表示されます。

class Toy(object):
    def __init__(self, body):
        self.body.owner = self

...

def collision(a, b, contacts):
    whatever(a.body.owner)

では、構造が共有ライブラリによって割り当て/解放された場合でも、構造がガベージコレクションされるようにするにはどうすればよいでしょうか。

詳細に興味がある場合は、リポジトリがあります:http: //bitbucket.org/cheery/ctypes-chipmunk/

4

3 に答える 3

3

やりたいこと、つまり、オブジェクトを割り当て、オブジェクトが使用されなくなったときに自動的に割り当てを解除するオブジェクトを作成することは、残念ながらPythonではほとんど不可能です。delステートメントが呼び出されることが保証されていないため、これに依存することはできません。

Pythonの標準的な方法は次のとおりです。

try:
    allocate()
    dostuff()
finally:
    cleanup()

または、2.5以降では、コンテキストマネージャーを作成してwithステートメントを使用することもできます。これは、これを行うための優れた方法です。

ただし、これらは両方とも、主にコードスニペットの先頭で割り当て/ロックする場合に使用されます。プログラムの実行全体にリソースを割り当てたい場合は、プログラムのメインコードを実行する前に、起動時にリソースを割り当て、後で割り当てを解除する必要があります。ここで説明されていない状況が1つあります。それは、多くのリソースを動的に割り当ておよび割り当て解除し、コード内の多くの場所でそれらを使用する場合です。たとえば、メモリバッファなどのプールが必要な場合です。しかし、これらのケースのほとんどは、Pythonが処理するメモリ用であるため、これらについて気にする必要はありません。もちろん、メモリではないものの動的なプール割り当てが必要な場合があります。次に、例で試行するタイプの割り当て解除が必要になりますPythonで行うのは難しい。

于 2009-06-25T14:33:21.093 に答える
0

weakrefが壊れていない場合、これは機能する可能性があると思います。

from weakref import ref

pointers = set()

class Pointer(object):
    def __init__(self, cfun, ptr):
        pointers.add(self)
        self.ref = ref(ptr, self.cleanup)
        self.data = cast(ptr, c_void_p).value # python cast it so smart, but it can't be smarter than this.
        self.cfun = cfun

    def cleanup(self, obj):
        print 'cleanup 0x%x' % self.data
        self.cfun(self.data)
        pointers.remove(self)

def cleanup(cfun, ptr):
    Pointer(cfun, ptr)

まだやってみます。重要な点は、ポインターには整数以外の外部ポインターへの強い参照がないことです。これは、ctypesがバインディングで解放する必要のあるメモリを解放しない場合に機能するはずです。ええ、それは基本的にハックですが、私が試していた以前のものよりもうまくいくかもしれないと思います。

編集:試してみましたが、コードを少し微調整した後でも機能するようです。驚くべきことに、すべての構造からデルを取り出しても、失敗しているように見えます。面白いがイライラする。

どちらも機能しません。奇妙なチャンスから、場所によって循環参照を削除することができましたが、物事は壊れたままです。

編集:まあ..weakrefsは結局壊れていました!したがって、明示的に強制することを除いて、Pythonで信頼性の高いクリーンアップを行うための解決策はおそらくありません。

于 2009-06-25T14:52:06.547 に答える
0

CPythonでは、__del__ オブジェクトの信頼できるデストラクタです。これは、参照カウントがゼロに達すると常に呼び出されるためです(注:__del__メソッドが定義されたアイテムの循環参照のように、参照カウントがゼロに達することはない場合がありますが、それは別の問題です)。

更新 コメントから、問題はオブジェクトの破棄の順序に関連していることがわかります。bodyグローバルオブジェクトであり、他のすべてのオブジェクトよりも先に破棄されているため、使用できなくなります。
実際、グローバルオブジェクトを使用するのは良くありません。このような問題だけでなく、メンテナンスも原因です。

次に、このようなものでクラスを変更します

class Body(object):
    def __init__(self, mass, inertia):
        self._bodyref = body
        self._body = body.New(mass, inertia)

    def __del__(self):
        print '__del__ %r' % self
        if body:
            body.Free(self._body)

...        

def set_velocity_func(self, func):
    self._body.contents.velocity_func = ctypes_wrapping(func)

いくつかのメモ:

  1. この変更は、グローバルボディオブジェクトへの参照を追加するだけであり、したがって、少なくともそのクラスから派生したすべてのオブジェクトと同じくらい存続します。
  2. それでも、単体テストとメンテナンスのため、グローバルオブジェクトの使用は適切ではありません。オブジェクトのファクトリを用意することをお勧めします。これにより、クラスに正しい「本体」が設定され、単体テストの場合は、モックオブジェクトを簡単に配置できます。しかし、それは本当にあなた次第であり、このプロジェクトでどれだけの努力が理にかなっていると思うかはあなた次第です。
于 2009-06-25T16:55:14.357 に答える