Wie lautet die Methode __del__? Wie wird sie aufgerufen?

108

Ich lese einen Code. Es gibt eine Klasse, in der die __del__Methode definiert ist. Ich habe herausgefunden, dass diese Methode verwendet wird, um eine Instanz der Klasse zu zerstören. Ich kann jedoch keinen Ort finden, an dem diese Methode verwendet wird. Der Hauptgrund dafür ist, dass ich nicht weiß, wie diese Methode angewendet wird, wahrscheinlich nicht so : obj1.del(). Meine Frage ist also, wie man die __del__Methode aufruft.

Verrtex
quelle

Antworten:

167

__del__ist ein Finalizer . Es wird aufgerufen, wenn ein Objekt durch Müll gesammelt wird. Dies geschieht irgendwann, nachdem alle Verweise auf das Objekt gelöscht wurden.

In einem einfachen Fall kann dies direkt nach dem Sagen del xoder, wenn xes sich um eine lokale Variable handelt, nach dem Ende der Funktion erfolgen. Insbesondere wird CPython (die Standard-Python-Implementierung), sofern keine Zirkelverweise vorhanden sind, sofort Speicherbereinigung durchführen.

Dies ist jedoch ein Implementierungsdetail von CPython. Die einzige erforderliche Eigenschaft der Garbage Collection Python ist , dass es passiert , nachdem alle Verweise gelöscht wurden, so könnte dies nicht notwendig passiert direkt nach und kann nicht passieren .

Darüber hinaus können Variablen aus vielen Gründen eine lange Lebensdauer haben , z. B. kann eine sich ausbreitende Ausnahme oder eine Modul-Introspektion die Anzahl der Variablenreferenzen über 0 halten. Außerdem kann die Variable Teil des Referenzzyklus sein - CPython mit aktivierter Garbage Collection unterbricht die meisten Unterbrechungen , aber nicht alle, solche Zyklen, und selbst dann nur periodisch.

Da Sie keine Garantie dafür haben, dass es ausgeführt wird, sollten Sie niemals den Code einfügen, in den Sie ausgeführt werden müssen. __del__()Stattdessen gehört dieser Code zur finallyKlausel des tryBlocks oder zu einem Kontextmanager in einer withAnweisung. Es gibt jedoch gültige Anwendungsfälle für __del__: Wenn ein Objekt beispielsweise Xauf Yeine YReferenzkopie in einem globalen cache( cache['X -> Y'] = Y) verweist und diese aufbewahrt, ist es höflich X.__del__, auch den Cache-Eintrag zu löschen.

Wenn Sie wissen, dass der Destruktor (unter Verstoß gegen die obige Richtlinie) eine erforderliche Bereinigung bereitstellt, möchten Sie diese möglicherweise direkt aufrufen , da sie als Methode nichts Besonderes enthält : x.__del__(). Natürlich sollten Sie dies nur tun, wenn Sie wissen, dass es nichts ausmacht, zweimal angerufen zu werden. Als letzten Ausweg können Sie diese Methode mit neu definieren

type(x).__del__ = my_safe_cleanup_method  
ilya n.
quelle
5
Sie sagen, dass die Funktion von CPython, ein Objekt unmittelbar nach dem Reduzieren seines Referenzzählers auf Null zu löschen, ein "Implementierungsdetail" ist. Ich bin nicht überzeugt. Können Sie einen Link zur Sicherung dieser Behauptung bereitstellen? (Ich meine, fette Schrift ist für sich genommen ziemlich überzeugend, aber Links sind eine knappe Sekunde ... :-)
Stuart Berg
14
Details zur CPython-Implementierung: CPython verwendet derzeit ein Referenzzählschema mit (optionaler) verzögerter Erkennung von zyklisch verknüpftem Müll. Andere Implementierungen verhalten sich anders und CPython kann sich ändern. ( docs.python.org/2/reference/datamodel.html )
ilya n.
Was ist mit __exit__in diesem Zusammenhang? Wird es nach oder vor __del__oder zusammen ausgeführt?
Lony
1
Enthält "möglicherweise überhaupt nicht", wenn das Programm beendet wird?
Andy Hayden
1
@AndyHayden: __del__Methoden werden möglicherweise nicht einmal beim Beenden des Programms ausgeführt, und selbst wenn sie beim Beenden ausgeführt werden, erfordert das Schreiben einer __del__Methode, die auch dann ordnungsgemäß funktioniert, wenn der Interpreter damit beschäftigt ist, sich selbst zu zerstören, eine sorgfältigere Codierung, als viele Programmierer anwenden. (Bei der CPython-Bereinigung werden normalerweise __del__Methoden ausgeführt, die beim Herunterfahren des Interpreters ausgeführt werden. Es gibt jedoch immer noch Fälle, in denen dies nicht ausreicht. Daemon-Threads, Globals auf C-Ebene und Objekte, __del__die in einem anderen erstellt wurden, __del__können dazu führen, dass __del__Methoden nicht ausgeführt werden.)
user2357112 unterstützt Monica
79

Ich habe die Antwort auf eine andere Frage geschrieben, obwohl dies eine genauere Frage ist.

Wie funktionieren Konstruktoren und Destruktoren?

Hier ist eine leicht meinungsgebundene Antwort.

Nicht benutzen __del__. Dies ist kein C ++ oder eine Sprache für Destruktoren. Die __del__Methode sollte in Python 3.x wirklich weg sein, obwohl ich sicher bin, dass jemand einen Anwendungsfall finden wird, der Sinn macht. Beachten Sie bei Bedarf __del__die grundlegenden Einschränkungen unter http://docs.python.org/reference/datamodel.html :

  • __del__wird aufgerufen, wenn der Garbage Collector zufällig die Objekte sammelt, nicht wenn Sie den letzten Verweis auf ein Objekt verlieren und nicht wenn Sie es ausführen del object.
  • __del__ist für das Aufrufen von Elementen __del__in einer Oberklasse verantwortlich, obwohl nicht klar ist, ob dies in der Reihenfolge der Methodenauflösung (MRO) erfolgt oder nur jede Oberklasse aufgerufen wird.
  • Mit einem __del__Mittel gibt der Garbage Collector das Erkennen und Bereinigen von zyklischen Links auf, z. B. den Verlust des letzten Verweises auf eine verknüpfte Liste. Sie können eine Liste der Objekte erhalten, die von gc.garbage ignoriert werden. Sie können manchmal schwache Referenzen verwenden, um den Zyklus insgesamt zu vermeiden. Dies wird ab und zu diskutiert: siehe http://mail.python.org/pipermail/python-ideas/2009-October/006194.html .
  • Die __del__Funktion kann betrügen, einen Verweis auf ein Objekt speichern und die Speicherbereinigung stoppen.
  • Ausnahmen, die explizit in __del__ausgelöst werden, werden ignoriert.
  • __del__ergänzt __new__weit mehr als __init__. Das wird verwirrend. Eine Erklärung und Fallstricke finden Sie unter http://www.algorithm.co.il/blogs/programming/python-gotchas-1- del- is-not-the-Gegenteil-von- init / .
  • __del__ist kein "geliebtes" Kind in Python. Sie werden feststellen, dass in der Dokumentation zu sys.exit () nicht angegeben ist, ob vor dem Beenden Müll gesammelt wird, und es gibt viele seltsame Probleme. Das Aufrufen von __del__on globals führt zu ungewöhnlichen Ordnungsproblemen, z . B. http://bugs.python.org/issue5099 . Sollte __del__angerufen werden, auch wenn das __init__fehlschlägt? Einen langen Thread finden Sie unter http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423 .

Andererseits:

Und mein pesonaler Grund, die __del__Funktion nicht zu mögen .

  • Jedes Mal, __del__wenn jemand etwas anspricht, entstehen dreißig verwirrende Botschaften.
  • Es zerbricht diese Gegenstände im Zen von Python:
    • Einfach ist besser als kompliziert.
    • Sonderfälle sind nicht speziell genug, um gegen die Regeln zu verstoßen.
    • Fehler sollten niemals stillschweigend vergehen.
    • Verweigern Sie angesichts von Zweideutigkeiten die Versuchung zu raten.
    • Es sollte einen - und vorzugsweise nur einen - offensichtlichen Weg geben, dies zu tun.
    • Wenn die Implementierung schwer zu erklären ist, ist es eine schlechte Idee.

Finden Sie also einen Grund, nicht zu verwenden __del__.

Charles Merriam
quelle
6
Auch wenn die Frage nicht genau lautet: Warum sollten wir nicht verwenden __del__, sondern wie Sie anrufen __del__, ist Ihre Antwort interessant.
nbro
Vielen Dank. Manchmal ist es die beste Idee, sich von schrecklichen Ideen fernzuhalten.
Charles Merriam
In anderen Nachrichten habe ich vergessen zu erwähnen, dass PyPy (ein schnellerer Interpreter für länger laufende Apps) bei del kaputt geht .
Charles Merriam
Vielen Dank an @Gloin für die Aktualisierung des defekten Links!
Charles Merriam
@CharlesMerriam Vielen Sie für die Antwort!
Tom Burrows
13

Die __del__Methode wird aufgerufen, wenn das Objekt durch Müll gesammelt wird. Beachten Sie jedoch, dass der Aufruf nicht unbedingt garantiert ist. Der folgende Code allein reicht nicht unbedingt aus:

del obj

Der Grund dafür ist, dass delnur die Referenzanzahl um eins verringert wird. Wenn etwas anderes einen Verweis auf das Objekt hat, __del__wird es nicht aufgerufen.

Es gibt jedoch einige Einschränkungen bei der Verwendung __del__. Im Allgemeinen sind sie normalerweise nicht sehr nützlich. Es klingt für mich eher so, als ob Sie eine Close-Methode oder vielleicht eine with-Anweisung verwenden möchten .

Weitere Informationen finden__del__ Sie in der Python-Dokumentation zu Methoden .

Eine andere Sache zu beachten: __del__Methoden können die Speicherbereinigung verhindern, wenn sie überbeansprucht werden. Insbesondere bei einem Zirkelverweis, der mehr als ein Objekt mit einer __del__Methode enthält, wird kein Müll gesammelt. Dies liegt daran, dass der Garbage Collector nicht weiß, welchen er zuerst aufrufen soll. Weitere Informationen finden Sie in der Dokumentation zum GC-Modul .

Jason Baker
quelle
8

Die __del__Methode (Rechtschreibung beachten!) Wird aufgerufen, wenn Ihr Objekt endgültig zerstört ist. Technisch gesehen (in cPython) bedeutet dies, dass keine Verweise mehr auf Ihr Objekt vorhanden sind, dh wenn es außerhalb des Gültigkeitsbereichs liegt.

Wenn Sie Ihr Objekt löschen und damit die __del__Methode aufrufen möchten, verwenden Sie

del obj1

Dadurch wird das Objekt gelöscht (vorausgesetzt, es gab keine anderen Verweise darauf).

Ich schlage vor, Sie schreiben eine kleine Klasse wie diese

class T:
    def __del__(self):
        print "deleted"

Und untersuchen Sie im Python-Interpreter, z

>>> a = T()
>>> del a
deleted
>>> a = T()
>>> b = a
>>> del b
>>> del a
deleted
>>> def fn():
...     a = T()
...     print "exiting fn"
...
>>> fn()
exiting fn
deleted
>>>   

Beachten Sie, dass Jython und Ironpython unterschiedliche Regeln haben, wann genau das Objekt gelöscht und __del__aufgerufen wird. __del__Aufgrund dessen und der Tatsache, dass sich das Objekt und seine Umgebung beim Aufrufen möglicherweise in einem unbekannten Zustand befinden, wird die Verwendung jedoch nicht als bewährte Methode angesehen . Es ist auch nicht absolut garantiert __del__, dass es aufgerufen wird - der Interpreter kann auf verschiedene Arten beendet werden, ohne alle Objekte zu löschen.

Nick Craig-Wood
quelle
1
verglichen mit stackoverflow.com/a/2452895/611007 und stackoverflow.com/a/1481512/611007 , use del obj1scheint wie eine schlechte Idee zu vertrauen.
n611x007
0

Wie bereits erwähnt, ist die __del__Funktionalität etwas unzuverlässig. In Fällen, in denen dies nützlich erscheint, sollten Sie stattdessen die Methoden __enter__und __exit__verwenden. Dies führt zu einem ähnlichen Verhalten wie die with open() as f: passSyntax für den Zugriff auf Dateien. __enter__genannt wird , automatisch , wenn der Umfang der Eingabe with, während __exit__automatisch beim Austritt aus aufgerufen wird. Weitere Informationen finden Sie in dieser Frage .

Somoria
quelle