Domanda

Ho bisogno di registrare una funzione atexit per l'uso con una classe (vedi Foo di seguito per un esempio) che, purtroppo, non ho modo diretto di pulizia tramite una chiamata di metodo: altro codice, che non ho il controllo sopra, le chiamate Foo.start() e Foo.end() ma a volte non chiama Foo.end() se incontra un errore, quindi ho bisogno di ripulire me stesso.

ho potuto usare qualche consiglio su chiusure in questo contesto:

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?
  • Sarà il lavoro di chiusura correttamente, per esempio in do_cleanup, è il valore di sé legata correttamente?

  • Come posso annullare la registrazione di una routine atexit ()?

  • C'è un modo migliore per fare questo?

modifica : questo è Python 2.6.5

È stato utile?

Soluzione

Fare un registro un registro globale e una funzione che chiama una funzione in esso, e rimuoverli da lì quando necessario.

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)

Altri suggerimenti

self è legato in modo corretto all'interno del callback a do_cleanup, ma in realtà, se tutto quello che state facendo è chiamare il metodo si potrebbe anche utilizzare il metodo vincolato direttamente.

Si utilizza atexit.unregister() per rimuovere la richiamata, ma c'è un problema qui come si deve annullare la registrazione la stessa funzione che avete registrato e dal momento che è stato utilizzato una funzione annidata che significa che si deve memorizzare un riferimento a tale funzione. Se seguite il mio suggerimento di usare un metodo vincolato, allora avete ancora salvare un riferimento ad esso:

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)

Si noti che è ancora possibile per il codice per uscire senza chiamare ther funzioni atexit registrato, ad esempio, se il processo viene interrotto con ctrl + pausa su Windows o ucciso con SIGABRT su linux.

Inoltre, come suggerisce un'altra risposta si potrebbe utilizzare __del__ ma che può essere problematico per la pulizia mentre un programma sta uscendo in quanto non può essere chiamato solo dopo altri globali di cui ha bisogno per l'accesso sono stati eliminati.

A cura notare che quando ho scritto questa risposta alla domanda non ha specificato Python 2.x. Vabbè, lascio la risposta qui comunque in caso aiuta chiunque altro.

Dal shanked cancellato il suo distacco, parlerò a favore di __del__ ancora:

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

Questo supporta i seguenti casi:

  • oggetti dove .end si chiama regolarmente; pulizia subito
  • oggetti che vengono rilasciati senza .end essere chiamato; pulizia quando l'ultimo riferimento va via
  • oggetti vivono in cicli; atexit pulizia
  • oggetti che sono tenuti in vita; atexit pulizia

Si noti che è importante che il gestore di pulizia contiene un riferimento debole all'oggetto, in quanto sarebbe altrimenti mantenere l'oggetto in vita.

Modifica : Cicli coinvolge Foo non sarà garbage collection, dal momento che Foo attrezzi __del__. Per consentire il ciclo di essere eliminati in fase di raccolta dei rifiuti, la pulizia deve essere messo fuori del ciclo.

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()

E 'importante che l'oggetto di pulitura non ha riferimenti indietro a Foo.

Credo che il codice va bene. Non c'è modo per annullare la registrazione, ma è possibile impostare un flag booleano che disabilitare la pulizia:

    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

Infine, tenere a mente che i gestori di atexit non ottenere chiamato se " il programma viene ucciso da un segnale non gestita da Python, quando viene rilevato un errore fatale in Python, o quando os._exit () viene chiamato. "

Perché non provare? Mi ci sono voluti solo un minuto per controllare.

(Risposta: Sì)

Tuttavia, è possibile semplificarla. Non è necessaria la chiusura.

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

E per non pulizia due volte, basta controllare nel metodo di pulitura, se è necessaria una pulizia o no prima di pulire.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top