Question

J'ai quelques liaisons de types, et pour chaque body.New, je devrais appeler body.Free. La bibliothèque que je lie ne possède pas de routines d'allocation isolées du reste du code (elles peuvent être appelées n'importe où), et pour utiliser quelques fonctionnalités utiles, j'ai besoin de faire des références cycliques.

Je pense que cela résoudrait si je trouvais un moyen fiable d’attacher un destructeur à un objet. (faibles références seraient utiles si elles me donnaient le rappel juste avant que les données ne soient supprimées.

Donc, évidemment, ce code mégafille quand je mets dans 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)

J'ai également essayé de résoudre ce problème par le biais de résolutions faibles, ce qui semble empirer les choses, mais de manière beaucoup plus imprévisible.

Même si je ne mets pas dans velocity_func, des cycles apparaîtront au moins à ce moment-là:

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

...

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

Alors, comment vous assurer que les structures seront récupérées, même si elles sont allouées / libérées par la bibliothèque partagée?

Il existe un référentiel si vous souhaitez plus de détails: http://bitbucket.org/cheery/ ctypes-chipmunk /

Était-ce utile?

La solution 3

Si les références faibles ne sont pas brisées, je suppose que cela peut fonctionner:

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)

Je l’essaye encore. La pièce importante est que le pointeur ne comporte aucune référence forte au pointeur étranger, à l'exception d'un entier. Cela devrait fonctionner si ctypes ne libère pas de la mémoire que je devrais libérer avec les liaisons. Oui, c’est fondamentalement un bidouillage, mais je pense que cela fonctionnera peut-être mieux que tout ce que j’avais essayé auparavant.

Edit: J'ai essayé, et cela semble fonctionner après un petit ajustement de mon code. Une chose surprenante est que même si je me suis retiré de toutes mes structures, cela semble toujours échouer. Intéressant mais frustrant.

Ni l'un ni l'autre ne fonctionne, par hasard, j'ai pu supprimer des références cycliques par endroits, mais les choses ne bougent pas.

Edit: Eh bien .. les faiblesses ont été brisées après tout! il n’ya donc probablement aucune solution pour un nettoyage fiable en python, si ce n’est que de le forcer à être explicite.

Autres conseils

Ce que vous voulez faire, c’est-à-dire créer un objet qui alloue des objets puis désalloue automatiquement lorsque l’objet n’est plus utilisé, est malheureusement presque impossible en Python. L'appel de del ne s'appelle pas forcément, vous ne pouvez donc pas vous en fier.

La méthode standard en Python est simplement:

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

Ou, depuis la version 2.5, vous pouvez également créer des gestionnaires de contexte et utiliser l'instruction with, ce qui est plus pratique.

Mais ces deux éléments sont principalement destinés à l’affectation / le verrouillage du début d’un extrait de code. Si vous souhaitez que des éléments soient alloués pour toute l'exécution du programme, vous devez allouer la ressource au démarrage, avant que le code principal du programme ne s'exécute, puis désallouer ultérieurement. Il existe une situation non traitée ici: vous souhaitez affecter et désallouer de nombreuses ressources de manière dynamique et les utiliser à de nombreux endroits dans le code. Par exemple, vous voulez un pool de mémoires tampons ou similaire. Mais la plupart de ces cas concernent la mémoire, que Python gérera pour vous, vous n'avez donc pas à vous en préoccuper. Il existe bien sûr des cas dans lesquels vous souhaitez une allocation dynamique de pool de choses qui ne sont PAS de la mémoire, puis vous souhaitez que le type de désallocation que vous essayez dans votre exemple et que soit difficile à faire avec Python. .

Dans CPython, __ del __ est un destructeur fiable d'un objet, car il sera toujours appelé lorsque le nombre de références atteindra zéro (remarque: il peut y avoir des cas, comme références circulaires d’articles avec la méthode __ del __ définie - le nombre de références n’atteignant jamais la valeur zéro, mais c’est un autre problème).

Mettre à jour D'après les commentaires, je comprends que le problème est lié à l'ordre de destruction des objets: body est un objet global, il est en train d'être détruit avant tous les autres objets. Il ne leur est donc plus disponible.
En fait, utiliser des objets globaux n’est pas bon; pas seulement à cause de problèmes comme celui-ci, mais aussi à cause de la maintenance.

Je changerais alors votre classe avec quelque chose comme ça

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)

Quelques notes:

  1. La modification consiste uniquement à ajouter une référence à l'objet global corps , qui vivra donc au moins autant que tous les objets dérivés de cette classe.
  2. Néanmoins, l'utilisation d'un objet global n'est pas satisfaisante en raison des tests et de la maintenance des unités. Il serait préférable d’avoir une usine pour l’objet, qui définira le "corps" correct. à la classe, et en cas de test unitaire mettra facilement un objet fictif. Mais c’est vraiment à vous de décider et des efforts que vous pensez avoir un sens dans ce projet.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top