2009-06-25 10 views
3

Ich habe einige Ctypes Bindings, und für jeden Körper.New sollte ich Body.Free nennen. Die Bibliothek, die ich bin, hat keine Zuweisungsroutinen, die vom Rest des Codes isoliert sind (sie können überall dort aufgerufen werden), und um einige nützliche Funktionen zu verwenden, die ich brauche, um zyklische Referenzen zu erstellen.Wie sauber in Python zuverlässig zu bereinigen?

Ich denke, es würde lösen, wenn ich eine zuverlässige Möglichkeit finden würde, Destruktor an ein Objekt zu haken. (Weakrefs würde helfen, wenn sie mir den Rückruf geben würde nur vor die Daten fallen gelassen wird

So offensichtlich diesen 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 zu lösen durch weakrefs, mit denen die Dinge scheinen immer nur noch schlimmer, nur nur weitgehend unvorhersehbare

Auch wenn ich nicht in der velocity_func setzen Sie, wird es Zyklen zumindest erscheint dann, 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 also sicherstellen, dass Strukturen Müll gesammelt werden, auch wenn sie von der gemeinsam genutzten Bibliothek freigegeben/freigegeben werden?

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

+1

Sehen Sie diese Frage: [http://stackoverflow.com/questions/865115/how-do-i-correctly-clean-up-a-python-object/865272#865272](http://stackoverflow .com/questions/865115/how-do-i-richtig-reinigungs-a-python-objekt/865272 # 865272) –

Antwort

0

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 es noch versuchen. Der wichtige Teil ist, dass der Zeiger keine starken Verweise auf den fremden Zeiger hat, außer einer Ganzzahl. Dies sollte funktionieren, wenn Ctypes keinen Speicher freigibt, den ich mit den Bindungen freigeben sollte. Ja, es ist im Grunde genommen ein Hack, aber ich denke, es könnte besser funktionieren als die früheren Dinge, die ich versucht habe.

Edit: Versuchte es, und es scheint nach kleinen Feinabstimmung meines Codes zu arbeiten.Eine überraschende Sache ist, dass, selbst wenn ich del aus allen meinen Strukturen herausbekommen habe, scheint es immer noch scheitern. Interessant, aber frustrierend.

Keine funktioniert, von einigen seltsamen Chance konnte ich zyklische Referenzen an Orten wegfallen, aber die Dinge bleiben pleite.

Edit: Nun .. Schwachstellen wurden schließlich gebrochen! Es gibt also wahrscheinlich keine Lösung für eine zuverlässige Säuberung in Python, außer dass es explizit sein muss.

3

Was wollen Sie tun, das ist ein Objekt erstellen, die Dinge ordnet und dann freigibt automatisch, wenn das Objekt ist nicht mehr in Gebrauch ist, ist fast unmöglich, in Python, leider. Die del Anweisung kann nicht garantiert aufgerufen werden, so dass Sie sich nicht darauf verlassen können.

Der üblicher Weg in Python ist einfach:

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

Oder seit 2.5 können Sie auch kontext Manager erstellen und verwenden die mit Aussage, die das zu tun einen ordentlichen Weg.

Aber beide sind in erster Linie für, wenn Sie am Anfang eines Code-Snippets zuordnen/sperren. Wenn Sie Dinge für den gesamten Programmlauf reservieren möchten, müssen Sie die Ressource beim Start zuweisen, bevor der Hauptcode des Programms ausgeführt wird, und danach die Zuordnung aufheben. Es gibt eine Situation, die hier nicht behandelt wird, und das ist, wenn Sie viele Ressourcen dynamisch zuweisen und freigeben möchten und sie an vielen Stellen im Code verwenden möchten. Zum Beispiel möchten Sie einen Pool von Speicherpuffern oder ähnlichem. Aber die meisten dieser Fälle sind für das Gedächtnis gedacht, das Python für dich behandeln wird, also musst du dich nicht darum kümmern. Es gibt natürlich Fälle, in denen Sie eine dynamische Pool-Zuweisung von Dingen haben möchten, die KEIN Speicher sind, und dann würden Sie die Art der Aufhebung der Zuweisung in Ihrem Beispiel wünschen, und dass mit Python schwierig zu tun ist.

0

In CPython, __del__ist ein zuverlässiger destructor eines Objekts, weil es immer dann aufgerufen wird, wenn der Referenzzähler Null (Note erreicht: es Fälle geben kann - wie zirkuläre Referenzen von Elementen mit __del__ Methode definiert - in dem die Referenzzählung wird nie Null erreichen, aber das ist ein anderes Problem).

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 ist es nicht mehr verfügbar zu ihnen.
Eigentlich ist die Verwendung globaler Objekte nicht gut; nicht nur wegen solcher Probleme, sondern auch wegen Wartungsarbeiten.

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

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 so lebe mindestens so viel wie alle Objekte, die von dieser Klasse abgeleitet sind.
  2. Dennoch ist die Verwendung eines globalen Objekts aufgrund von Komponententests und Wartung nicht gut; besser wäre es, eine Fabrik für das Objekt zu haben, die der Klasse den richtigen "Körper" gibt, und im Falle eines Komponententests wird leicht ein Mockobjekt gesetzt. Aber das liegt wirklich an Ihnen und wie viel Aufwand Sie in diesem Projekt für sinnvoll halten.
+0

Ist __del__ zuverlässig oder raten Sie nur? "Zirkelverweise, bei denen es sich um Müll handelt, werden erkannt, wenn der Optionszyklusdetektor aktiviert ist (standardmäßig aktiviert). Sie können jedoch nur bereinigt werden, wenn keine __del __() - Methoden auf Python-Ebene vorhanden sind." – Cheery

+1

Auch das, was Sie beschrieben haben, ist kein Fehler. Wenn meine Apps geschlossen werden, gibt es viele ignorierte Ausnahmen, die anschreien, wie sie die Methode "Free" von None aufrufen können. Ich denke, aus irgendeinem Grund entfernt Python das Modul vor dem __del__, was in Anbetracht der Situation etwas ironisch ist. – Cheery

+0

__del__ ist NICHT zuverlässig. Sie können sich nicht darauf verlassen, dass __del__ jemals angerufen wird. Es kann daher nur verwendet werden, wenn die Ressource eine Ressource ist, die das Betriebssystem selbst bereinigt, wenn die Anwendung beendet wird, dh Speicher und Dateihandles etc. –