Domanda

Ho alcuni attacchi di tipo e per ogni corpo. Nuovo, dovrei chiamare body.Free. La libreria che sto vincolando non ha routine di allocazione isolate dal resto del codice (possono essere chiamate ovunque) e per usare un paio di utili funzioni ho bisogno di fare riferimenti ciclici.

Penso che risolverebbe se trovassi un modo affidabile per agganciare il distruttore a un oggetto. (weakrefs sarebbe d'aiuto se mi dessero il callback appena prima i dati venivano eliminati.

Quindi ovviamente questo codice megafail quando inserisco 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)

Ho anche cercato di risolverlo attraverso i deboli riflessi, con quelle cose che sembrano peggiorare, solo in gran parte più imprevedibili.

Anche se non inserisco velocity_func, almeno appariranno dei cicli quando lo faccio:

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

...

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

Quindi, come assicurarsi che le Strutture vengano raccolte spazzatura, anche se sono allocate / liberate dalla libreria condivisa?

C'è un repository se sei interessato a maggiori dettagli: http://bitbucket.org/cheery/ ctypes-chipmunk /

È stato utile?

Soluzione 3

Se i punti deboli non sono rotti, immagino che ciò possa funzionare:

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)

Lo provo ancora. Il pezzo importante è che il puntatore non ha alcun riferimento forte al puntatore esterno, tranne un numero intero. Questo dovrebbe funzionare se Ctypes non libera la memoria che dovrei liberare con i binding. Sì, è fondamentalmente un trucco, ma penso che potrebbe funzionare meglio delle cose precedenti che ho provato.

Modifica: l'ho provato e sembra funzionare dopo una piccola messa a punto del mio codice. Una cosa sorprendente è che, anche se ho estratto del da tutte le mie strutture, sembra che fallisca ancora. Interessante ma frustrante.

Né funziona, per una strana occasione sono stato in grado di eliminare i riferimenti ciclici in alcuni punti, ma le cose restano al verde.

Modifica: Beh ... i deboli sono stati rotti dopo tutto! quindi probabilmente non esiste una soluzione per una pulizia affidabile in Python, se non quella di forzarlo ad essere esplicito.

Altri suggerimenti

Ciò che vuoi fare, ovvero creare un oggetto che alloca le cose e poi si disloca automaticamente quando l'oggetto non è più in uso, sfortunatamente è quasi impossibile in Python. L'istruzione del non è garantita per essere chiamata, quindi non puoi fare affidamento su di essa.

Il modo standard in Python è semplicemente:

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

O dal 2.5 puoi anche creare gestori di contesto e usare l'istruzione with, che è un modo più semplice di farlo.

Ma entrambi sono principalmente utili quando si alloca / si blocca all'inizio di uno snippet di codice. Se si desidera disporre di elementi allocati per l'intera esecuzione del programma, è necessario allocare la risorsa all'avvio, prima dell'esecuzione del codice principale del programma e successivamente deallocare. Esiste una situazione che non è trattata qui, ovvero quando si desidera allocare e deallocare dinamicamente molte risorse e utilizzarle in molti punti del codice. Ad esempio, vuoi un pool di buffer di memoria o simili. Ma la maggior parte di questi casi riguarda la memoria, che Python gestirà per te, quindi non devi preoccuparti di quelli. Esistono ovviamente casi in cui si desidera disporre di un'allocazione dinamica di pool di cose che NON sono memoria e quindi si desidera il tipo di deallocazione che si prova nell'esempio e che è complicato da fare con Python .

In CPython, __del__ è un distruttore affidabile di un oggetto, perché verrà sempre chiamato quando il conteggio dei riferimenti raggiunge lo zero (nota: potrebbero esserci casi come riferimenti circolari di elementi con il metodo __del__ - in cui il conteggio dei riferimenti non raggiungerà mai lo zero, ma questo è un altro problema).

Aggiorna Dai commenti, capisco che il problema è legato all'ordine di distruzione degli oggetti: corpo è un oggetto globale e viene distrutto prima di tutti gli altri oggetti, quindi non è più disponibile per loro.
In realtà, l'uso di oggetti globali non è buono; non solo per problemi come questo, ma anche per manutenzione.

Vorrei quindi cambiare classe con qualcosa del genere

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)

Un paio di note:

  1. La modifica aggiunge solo un riferimento all'oggetto body globale, che quindi vivrà almeno quanto tutti gli oggetti derivati ??da quella classe.
  2. Tuttavia, l'uso di un oggetto globale non va bene a causa di test e manutenzione dell'unità; meglio sarebbe avere una fabbrica per l'oggetto, che imposterà il "corpo" corretto alla classe, e in caso di unit test metterà facilmente un oggetto finto. Ma dipende davvero da te e quanto sforzo pensi abbia senso in questo progetto.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top