Question

je dois enregistrer une fonction atexit pour une utilisation avec une classe (voir Foo ci-dessous pour un exemple) que, malheureusement, je n'ai aucun moyen direct de nettoyage au moyen d'un appel de méthode: autre code, que je n'ai pas le contrôle plus, les appels Foo.start() et Foo.end() mais parfois ne pas appeler Foo.end() si elle rencontre une erreur, alors je dois nettoyer moi-même.

Je pourrais utiliser dans ce contexte, quelques conseils sur les fermetures:

class Foo:
  def cleanup(self):
     # do something here
  def start(self):
     def do_cleanup():
        self.cleanup()
     atexit.register(do_cleanup)
  def end(self):
     # cleanup is no longer necessary... how do we unregister?
  • Est-ce que le travail de fermeture correctement, par exemple en do_cleanup, est la valeur de soi liée correctement?

  • Comment puis-je désinscription une routine atexit ()?

  • Y at-il une meilleure façon de le faire?

modifier : c'est Python 2.6.5

Était-ce utile?

La solution

Faire un registre un registre global et une fonction qui appelle une fonction, et les enlever de là en cas de besoin.

cleaners = set()

def _call_cleaners():
    for cleaner in list(cleaners):
        cleaner()

atexit.register(_call_cleaners)

class Foo(object):
  def cleanup(self):
     if self.cleaned:
         raise RuntimeError("ALREADY CLEANED")
     self.cleaned = True
  def start(self):
     self.cleaned = False
     cleaners.add(self.cleanup)
  def end(self):
     self.cleanup()
     cleaners.remove(self.cleanup)

Autres conseils

self est correctement lié à l'intérieur du rappel à do_cleanup, mais en fait, si tout ce que vous faites est d'appeler la méthode vous pourriez aussi bien utiliser la méthode liée directement.

Vous utilisez atexit.unregister() pour supprimer le rappel, mais il y a un hic ici que vous devez désenregistrer la même fonction que vous avez enregistré et que vous avez utilisé une fonction imbriquée qui signifie que vous devez stocker une référence à cette fonction. Si vous suivez ma suggestion d'utiliser une méthode liée, vous devez toujours enregistrer une référence à elle:

class Foo:
  def cleanup(self):
     # do something here
  def start(self):
     self._cleanup = self.cleanup # Need to save the bound method for unregister
     atexit.register(self._cleanup)
  def end(self):
     atexit.unregister(self._cleanup)

Notez qu'il est toujours possible pour votre code pour quitter sans appeler utres atexit fonctions enregistrées, par exemple, si le processus est interrompu avec ctrl + break sur les fenêtres ou tué avec SIGABRT sur linux.

En outre comme une autre réponse que vous pouvez simplement utiliser suggère __del__ mais qui peut être problématique pour le nettoyage pendant qu'un programme est sortie car il ne peut être appelé qu'après d'autres GLOBALS dont il a besoin d'accès ont été supprimés.

Sous la direction de noter que lorsque je l'ai écrit cette réponse la question n'a pas précisé Python 2.x. Eh bien, je vais laisser la réponse ici de toute façon dans le cas où il aide quelqu'un d'autre.

Depuis cambrure supprimé son affectation, je parlerai en faveur de nouveau __del__:

import atexit, weakref
class Handler:
    def __init__(self, obj):
        self.obj = weakref.ref(obj)
    def cleanup(self):
        if self.obj is not None:
            obj = self.obj()
            if obj is not None:
                obj.cleanup()

class Foo:
    def __init__(self):
        self.start()

    def cleanup(self):
        print "cleanup"
        self.cleanup_handler = None

    def start(self):
        self.cleanup_handler = Handler(self)
        atexit.register(self.cleanup_handler.cleanup)

    def end(self):
        if self.cleanup_handler is None:
            return
        self.cleanup_handler.obj = None
        self.cleanup()

    def __del__(self):
        self.end()

a1=Foo()
a1.end()
a1=Foo()
a2=Foo()
del a2
a3=Foo()
a3.m=a3

prend en charge les cas suivants: les

  • objets où .end est appelé régulièrement; nettoyage tout de suite
  • Les objets qui sont libérés sans .end appelé; lorsque le dernier nettoyage référence disparaît
  • objets vivants dans les cycles; atexit nettoyage
  • objets
  • qui sont gardés en vie; atexit nettoyage

Notez qu'il est important que le gestionnaire de nettoyage contient une référence faible à l'objet, car il serait autrement garder l'objet vivant.

Modifier : Cycles Foo impliquant ne sera pas car Foo instruments __del__ ramasse-miettes,. Pour permettre le cycle suppression au moment de la collecte des ordures, le nettoyage doit être mis hors du cycle.

class Cleanup:
    cleaned = False
    def cleanup(self):
        if self.cleaned:
            return
        print "cleanup"
        self.cleaned = True
    def __del__(self):
        self.cleanup()

class Foo:
    def __init__(self):...
    def start(self):
        self.cleaner = Cleanup()
        atexit.register(Handler(self).cleanup)
    def cleanup(self):
        self.cleaner.cleanup()
    def end(self):
        self.cleanup()

Il est important que l'objet de nettoyage n'a pas de références à foo.

Je pense que le code est bien. Il n'y a pas moyen de désinscription, mais vous pouvez définir un indicateur booléen qui désactiverait le nettoyage:

    class Foo:
      def __init__(self):
         self.need_cleanup = True
      def cleanup(self):
         # do something here
         print 'clean up'
      def start(self):
         def do_cleanup():
            if self.need_cleanup:
               self.cleanup()
         atexit.register(do_cleanup)
      def end(self):
         # cleanup is no longer necessary... how do we unregister?
         self.need_cleanup = False

Enfin, gardez à l'esprit que les gestionnaires de atexit ne soit pas appelé si « le programme est tué par un signal non manipulé par Python, lorsqu'une erreur fatale interne python est détectée, ou lorsque os._exit () est appelée. "

Pourquoi ne pas essayer? Il ne m'a fallu une minute pour vérifier.

(Réponse: Oui)

Cependant, vous pouvez simplifier. La fermeture est pas nécessaire.

class Foo:
   def cleanup(self):
      pass
   def start(self):
      atexit.register(self.cleanup)

Et pour ne pas le nettoyage deux fois, il suffit de cocher dans la méthode de nettoyage si un nettoyage est nécessaire ou non avant de nettoyer.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top