2015-02-03 6 views
5

Auf den ersten Blick scheint es so, als ob Pythons __del__ spezielle Methode die gleichen Vorteile bietet, die ein Destruktor in C++ hat. Aber laut der Python-Dokumentation (https://docs.python.org/3.4/reference/datamodel.html) gibt es keine Garantie, dass die Methode Ihres Objekts __del__ jemals überhaupt aufgerufen wird!RAII in Python: Was ist der Sinn von __del__?

Es ist nicht garantiert, dass __del__() -Methoden für Objekte aufgerufen werden, die beim Beenden des Interpreters noch vorhanden sind.

Mit anderen Worten, die Methode ist nutzlos! Ist es nicht? Eine Hook-Funktion, die aufgerufen werden kann oder nicht, hilft nicht wirklich, also bietet __del__ nichts in Bezug auf RAII. Wenn ich einige wesentliche Aufräumarbeiten habe, brauche ich es nicht einige Zeit, oh, wenn jemals der GC fühlt es wirklich, ich brauche es zuverlässig, deterministisch und 100% der Zeit laufen.

Ich weiß, dass Python bietet Kontext-Manager, die für diese Aufgabe viel nützlicher sind, aber warum wurde __del__ überhaupt gehalten? Was ist der Punkt?

+0

Ich verstehe nicht wirklich, wonach genau Sie fragen. Ist Ihre Frage "Ist' __del__' im Wesentlichen nutzlos? "? (Ich glaube, es ist Ihre Frage) In diesem Fall glaube ich, dass Sie die Frage klären würden, indem Sie alle Verweise auf C++ oder RAII entfernen, die völlig unabhängig sind. Wenn Ihre Frage lautet: "Wie implementiere ich RAII in Python? Darf ich dafür' __del__' verwenden? " dann musst du wahrscheinlich eine Menge Dinge in deiner Frage ändern. – Bakuriu

+0

@Bakuriu Der Kern der Sache ist, was genau ist der Sinn von'__del__'? Kann jemand einen realistischen Anwendungsfall für die Methode nennen? – antred

Antwort

1

Weil __del__ aufgerufen wird. Es ist nur unklar, wann es so sein wird, denn in CPython, wenn Sie Zirkelverweise haben, kann der Refcount-Mechanismus die Objektrückgewinnung (und somit dessen Finalisierung über __del__) nicht übernehmen und muss sie an den Garbage Collector delegieren. Der Müllsammler hat dann ein Problem: Er kann nicht wissen, in welcher Reihenfolge die Zirkelbezüge zu brechen sind, weil dies zusätzliche Probleme auslösen kann (zB den Speicher freigibt, der bei der Fertigstellung eines anderen Teiles benötigt wird) der gesammelten Schleife, Auslösen eines segfault). Der Punkt, den Sie betonen, liegt darin, dass der Interpreter möglicherweise aus Gründen, die die Säuberung verhindern, beendet wird (z. B. segfolds oder ein C-Modul ruft exital exit() auf).

Es gibt PEP 442 für die sichere Objekt-Finalisierung, die in 3.4 abgeschlossen wurde. Ich schlage vor, Sie sehen es sich an.

https://www.python.org/dev/peps/pep-0442/

+0

Was wieder deutlich macht, wie nutzlos '__del__' ist. Zum Beispiel, wenn ich Objekte einer Mutex-Wächter-Klasse verwenden möchte, konnte ich das Aufschließen nicht in der '__del__' -Methode der Wächter-Klasse tun, weil es nicht genug ist zu wissen, dass' __del__' aufgerufen wird, wenn der GC sich anfühlt macht seinen Job ... Ich brauche den Mutex, der sofort freigegeben wird, wenn das Schutzobjekt "aus dem Geltungsbereich verschwindet" (ich weiß, dass es sich bei Pythons Scoping-Regeln ausdehnt). IMO '__del__' gibt Programmierern den falschen Eindruck, dass RAII in Python über Destruktoren machbar ist, wenn dies nicht der Fall ist. Sie müssen dafür Kontext-Manager verwenden. – antred

+1

Es ist nicht nutzlos. Sie versuchen, C++ in Python zu codieren. Der Punkt ist, dass "__del__" kein Destruktor ist. Es ist ein Mechanismus, der vorhanden ist und in sehr begrenzten Fällen nützlich sein kann, aber Sie können sich nicht darauf verlassen, ähnlich dem Java Finalizer. Siehe diesen Thread für eine ähnliche Frage http://stackoverflow.com/questions/158174/why-would-you-ever-implement-finalize –

9

__del__ ist ein Finalizerthread. Es ist kein Destruktor. Finalizer und Destruktoren sind völlig verschiedene Tiere.

Destruktoren werden zuverlässig bezeichnet und existieren nur in Sprachen mit deterministischer Speicherverwaltung (wie C++). Pythons Kontextmanager (die with-Anweisung) können unter bestimmten Umständen ähnliche Effekte erzielen. Diese sind zuverlässig, weil die Lebensdauer eines Objekts genau festgelegt ist; In C++ sterben Objekte, wenn sie explizit sind oder wenn ein Bereich verlassen wird (oder wenn ein intelligenter Zeiger sie als Reaktion auf seine eigene Zerstörung löscht). Und dann laufen Destruktoren.

Finalizer werden nicht zuverlässig aufgerufen. Die einzige gültige Verwendung eines Finalizers ist an emergency safety net (NB: dieser Artikel ist aus einer .NET-Perspektive geschrieben, aber die Konzepte übersetzen ziemlich gut). Zum Beispiel schließen sich die Dateiobjekte, die von open() zurückgegeben werden, automatisch bei Abschluss. Aber Sie sollten sie immer noch selbst schließen (z. B. mit der with-Anweisung). Dies liegt daran, dass die Objekte vom Garbage Collector dynamisch zerstört werden, was sofort passieren kann oder auch nicht, und bei der generationsbedingten Garbage Collection kann sie einige Objekte in einem bestimmten Durchgang sammeln oder nicht.Da niemand weiß, welche Optimierungen wir in Zukunft entwickeln könnten, ist es am sichersten anzunehmen, dass Sie nicht wissen können, wann der Garbage Collector zum Sammeln Ihrer Objekte kommt. Das heißt, Sie können sich nicht auf Finalizer verlassen.

Im speziellen Fall von CPython, erhalten Sie etwas stärkere Garantien, dank der Verwendung von Referenzzählung (die viel einfacher und vorhersehbarer als Garbage Collection). Wenn Sie sicherstellen können, dass Sie niemals einen Referenzzyklus für ein bestimmtes Objekt erstellen, wird der Finalizer dieses Objekts an einem vorhersagbaren Punkt aufgerufen (wenn die letzte Referenz stirbt). Dies ist nur gilt für CPython, die Referenz-Implementierung und nicht von PyPy, Ironpython, Jython oder andere Implementierungen.

+1

Eines der Zitate auf der Seite verlinkt sind Sie > Ein richtig geschriebenes Programm ist kann nicht davon ausgehen, dass Finalizer jemals laufen werden. Dies verstärkt nur meine Überzeugung, dass sie im Wesentlichen nutzlos sind. Selbst Ihre Bemerkung, dass sie als Notfall-Sicherheitsnetz verwendet werden kann, um Dinge zu bereinigen, die der Programmierer vergessen hat, ändert das nicht wirklich, weil es wiederum passieren kann oder auch nicht. Jedenfalls halte ich das für eine schreckliche Idee. Es schafft nur ein falsches Gefühl der Sicherheit, schafft Verwirrung und fördert die schlampige Ressourcenverwaltung (hey, ich muss nicht aufräumen, Python __might__ tue es einfach für mich). – antred

+1

Ich stimme dir zu. Deshalb verwende ich nie Finalizer. OTOH, die Finalisten, die an schwache Referenzen angehängt sind, können gelegentlich nützlich sein, wenn Sie zum Beispiel eine Sammlung von Live-Objekten pflegen müssen, diese aber nicht am Leben erhalten wollen. Weitere Informationen finden Sie unter "weakref". – Kevin

+0

PyPy garantiert, dass Ihr __del__ jedoch aufgerufen wird. In Cpython, wenn es ein Teil des Zyklus ist, wird es nicht AT ALL aufgerufen. – fijal