Frage

Ich muss eine registrieren atexit Funktion zur Verwendung mit einer Klasse (siehe Foo Im Folgenden für ein Beispiel), dass ich leider keine direkte Möglichkeit habe, über einen Methodenaufruf aufzuräumen: Andere Code, über die ich keine Kontrolle habe, Anrufe Foo.start() und Foo.end() aber manchmal nicht anruft Foo.end() Wenn es auf einen Fehler stößt, muss ich mich aufräumen.

Ich könnte in diesem Zusammenhang einige Ratschläge zu Schließungen verwenden:

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?
  • Wird der Verschluss ordnungsgemäß funktionieren, z. B. in do_cleanup, Ist der Wert der Selbstgebühr richtig?

  • Wie kann ich eine texit () Routine nicht registrieren?

  • Gibt es einen besseren Weg, dies zu tun?

bearbeiten: Dies ist Python 2.6.5

War es hilfreich?

Lösung

Machen Sie ein Register zu einer globalen Registrierung und einer Funktion, die eine Funktion darin aufruft, und entfernen Sie sie bei Bedarf von dort.

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)

Andere Tipps

self ist korrekt im Rückruf an do_cleanup gebunden, aber wenn Sie nur die Methode aufrufen, können Sie die gebundene Methode auch direkt verwenden.

Sie nutzen atexit.unregister() Um den Rückruf zu entfernen, gibt es hier jedoch einen Haken, da Sie dieselbe Funktion nicht registrieren müssen, die Sie registriert haben, und da Sie eine verschachtelte Funktion verwendet haben, die bedeutet, dass Sie einen Verweis auf diese Funktion speichern müssen. Wenn Sie meinem Vorschlag, eine gebundene Methode zu verwenden, folgen, müssen Sie immer noch einen Verweis darauf speichern:

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)

Beachten Sie, dass Ihr Code immer noch möglich ist, zu beenden, ohne dies anzurufen atexit Registrierte Funktionen, beispielsweise wenn der Prozess mit Strg+Break unter Windows abgebrochen oder mit Sigabrt unter Linux getötet wird.

Auch wie eine andere Antwort darauf hinweist, dass Sie nur verwenden können __del__ Dies kann jedoch für die Reinigung problematisch sein, während ein Programm beendet wird, da es möglicherweise erst nach anderen Globalen aufgerufen wird, auf die er zugreifen muss, um gelöscht zu werden.

Bearbeitet, um festzustellen, dass die Frage, als ich diese Antwort schrieb, Python 2.x nicht angegeben hat. Na ja, ich werde die Antwort hier trotzdem hinterlassen, falls es jedem anderen hilft.

Da ich seine Posting gelöscht habe __del__ wieder:

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

Dies unterstützt die folgenden Fälle:

  • Objekte, bei denen .end regelmäßig genannt wird; sofort aufräumen
  • Objekte, die freigegeben werden, ohne zu bezeichnen; Aufräumarbeiten Wenn die letzte Referenz verschwindet
  • Objekte, die in Zyklen leben; Reinigung von ATEXIT
  • Objekte, die am Leben gehalten werden; Reinigung von ATEXIT

Beachten Sie, dass es wichtig ist, dass der Aufräumarbeiten einen schwachen Hinweis auf das Objekt enthält, da er das Objekt sonst am Leben erhalten würde.

Bearbeiten: Zyklen mit FOO werden nicht mit Müll gesammelt, da Foo implementiert __del__. Um den Zyklus zur Müllabfuhrzeit gelöscht zu können, muss die Reinigung aus dem Zyklus herausgenommen werden.

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

Es ist wichtig, dass das Reinigungsobjekt keine Referenzen auf Foo hat.

Ich denke, der Code ist in Ordnung. Es gibt keine Möglichkeit, sich nicht zu registrieren, aber Sie können eine booleale Flagge festlegen, die die Reinigung deaktiviert würde:

    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

Zuletzt denken Sie daran, dass atexit Handler werden nicht angerufen, wenn "Das Programm wird durch ein von Python nicht behandeltes Signal getötet, wenn ein python -tödlicher interner Fehler festgestellt wird oder wenn os._exit () aufgerufen wird."

Warum versuchst du es nicht? Ich brauchte nur eine Minute, um zu überprüfen.

(Antwort: Ja)

Sie können es jedoch vereinfachen. Die Schließung ist nicht benötigt.

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

Um nicht zweimal aufzuräumen, überprüfen Sie einfach die Reinigungsmethode, wenn eine Reinigung benötigt wird oder nicht, bevor Sie aufräumen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top