Frage

Ich habe einige ctypes Bindungen und für jede body.New sollte ich anrufen body.Free. Die Bibliothek Ich bin Bindung nicht Zuweisungsroutinen haben aus dem Rest des Codes isoliert aus (sie können überall dort genannt werden), und einige nützliche Funktionen zu verwenden, ich zyklische Referenzen vornehmen müssen.

Ich denke, es würde lösen, wenn ich eine zuverlässige Art und Weise destructor zu einem Objekt Haken finden würde. (Weakrefs würde helfen, wenn sie mir den Rückruf geben würde kurz vor dem die Daten fallen gelassen wird.

So offensichtlich dieser Code megafails, wenn ich in velocity_func setzen:

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)

Ich habe auch versucht, es durch weakrefs zu lösen, die mit den Dinge scheinen immer nur noch schlimmer, nur nur weitgehend unvorhersehbar.

Auch wenn ich in der velocity_func nicht setzen, wird es Zyklen zumindest dann angezeigt, wenn ich dies tun:

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

...

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

Wie um sicherzustellen, dass Strukturen Müll gesammelt bekommen, auch wenn sie von der gemeinsam genutzten Bibliothek zugeordnet / freigegeben werden?

Es gibt Repository, wenn Sie interessiert sind, über weitere Details sind: http://bitbucket.org/cheery/ ctypes-chipmunk /

War es hilfreich?

Lösung 3

Wenn weakrefs nicht gebrochen werden, ich denke, dies funktionieren kann:

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)

Ich versuche es noch nicht. Das wichtige ist, dass der Zeiger keine starken Hinweise auf den ausländischen Zeiger, mit Ausnahme einer ganzen Zahl hat. Dies sollte funktionieren, wenn ctypes nicht freie Speicher, die ich mit den Bindungen befreien sollte. Ja, es ist im Grunde ein Hack, aber ich denke, es kann besser funktionieren als die früheren Dinge, die ich habe versucht.

Edit: Versuchte es, und es scheint nach kleinen zu arbeiten, um meinen Code Feinstimm. Eine überraschende Sache ist, dass selbst wenn ich bekam del aus allen meinen Strukturen, es immer noch nicht zu sein scheinen. Interessant, aber frustrierend.

Weder Werke, von einigen seltsamen Zufall konnte ich weg zyklische Referenzen in Orte fallen, aber die Dinge bleiben pleite.

Edit: Nun .. weakrefs WURDEN schließlich gebrochen! so gibt es wahrscheinlich keine Lösung für die zuverlässige Bereinigung in Python, außer als zwingt sie explizit zu sein.

Andere Tipps

Was Sie tun wollen, ist ein Objekt zu erstellen, die Dinge und dann freigibt automatisch zuordnet, wenn das Objekt nicht mehr in Gebrauch ist, ist in Python fast unmöglich, leider. Die del Anweisung wird nicht genannt werden garantiert, so kann man nicht darauf verlassen.

Der üblicher Weg in Python ist einfach:

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

oder seit 2.5 Sie können auch kontext Manager erstellen und verwenden die mit Anweisung, die einen sauberere Weg, dies zu tun.

Aber diese beiden sind in erster Linie für, wenn Sie vergeben / lock am Anfang eines Code-Schnipsel. Wenn Sie die Dinge für den ganzen Lauf des Programms zugewiesen haben wollen, müssen Sie die Ressource beim Start zuweisen, bevor der Hauptcode des Programms ausgeführt wird, und danach freigeben. Es ist eine Situation, die hier nicht abgedeckt ist, und das ist, wenn Sie zuordnen mögen, und viele Ressourcen freigeben und sie dynamisch an vielen Stellen im Code verwenden. Zum Beispiel einen Pool von Speicherpuffern oder ähnlichem will. Aber die meisten dieser Fälle sind für das Gedächtnis, die Python wird für Sie handhaben, so dass Sie sich über diese nicht zu stören. Natürlich gibt es auch Fälle, in denen Sie dynamische Poolzuordnung der Dinge haben wollen, die NICHT-Speicher sind, und dann würden Sie die Art der Aufhebung der Zuordnung Sie in Ihrem Beispiel versuchen wollen, und dass ist tricky mit Python zu tun .

In CPython, __del__ ist ein zuverlässiger destructor eines Objekts, weil es immer dann aufgerufen wird, wenn der Referenzzähler Null erreicht (Hinweis: Es kann vorkommen, dass - wie zirkuläre Referenzen von Elementen mit __del__ Verfahren definiert -. wo der Referenzzähler niemals Null erreicht wird, aber das ist ein anderes Thema)

Aktualisieren Aus den Kommentaren, ich verstehe das Problem der Reihenfolge der Zerstörung von Objekten zusammenhängt: Körper ist ein globales Objekt, und es vor allen anderen Objekten zerstört wird, so dass es ihnen nicht mehr zur Verfügung steht.
Eigentlich ist globale Objekte mit nicht gut; nicht nur wegen der Probleme wie diese, sondern auch wegen der Wartung.

Ich würde dann die Klasse mit so etwas wie dies ändern

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)

Ein paar Anmerkungen:

  1. Die Änderung wird nur das Hinzufügen eines Verweises auf die globale Körper Objekt, das wird also zumindest so viel wie alle Objekte aus dieser Klasse abgeleitet leben.
  2. Still, ein globales Objekt verwendet, ist nicht gut, weil die Unit-Tests und Wartung; besser wäre es, eine Fabrik für das Objekt zu haben, dass der richtigen „Körper“ auf die Klasse festgelegt wird, und im Fall von Unit-Test leicht ein Mock-Objekt setzen. Aber das ist wirklich an Ihnen und wie viel Mühe denken Sie Sinn in diesem Projekt.
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top