Stützt sich __del __ () auf die Bereinigung in Python unzuverlässig?

8

Ich habe über verschiedene Methoden gelesen, um Objekte in Python zu bereinigen, und ich bin auf diese Fragen gestoßen ( 1 ). 2 ), die im Grunde sagen, dass das Bereinigen mit __del__() unzuverlässig ist und der folgende Code vermieden werden sollte:

%Vor%

Das Problem ist, dass ich genau diesen Code verwende, und ich kann keine der in den obigen Fragen genannten Probleme reproduzieren. Soweit ich weiß, kann ich die Alternative nicht mit with aussagen, da ich Python zur Verfügung stelle Modul für eine Closed-Source-Software (testIDEA, anyone?) Diese Software erstellt Instanzen bestimmter Klassen und entsorgen sie, diese Instanzen müssen bereit sein, dazwischen liegende Dienste bereitzustellen. Die einzige Alternative zu __del__() , die ich sehe, ist das manuelle Aufrufen von open() und close() nach Bedarf, was vermutlich ziemlich fehleranfällig ist.

Ich verstehe, dass wenn ich den Interpreter schließe, es keine Garantie gibt, dass meine Objekte korrekt zerstört werden (und es stört mich nicht besonders, hey, selbst Python-Autoren haben entschieden, dass es OK war). Abgesehen davon spiele ich mit Feuer, indem ich __del__() für die Säuberung verwende?

PS: Ich habe anfangs darüber nachgedacht, eine Meta-Rede über Fragen mit Fehlern zu schreiben, die nicht reproduziert werden können, habe aber festgestellt, dass ich nicht die technischen Details bekomme, die ich auf diese Weise brauche.

    
Dmitry Grigoryev 18.02.2016, 17:45
quelle

2 Antworten

3

Sie beobachten das typische Problem mit Finalizern in Garbage Collection-Sprachen. Java hat es, C # hat es und sie alle bieten eine bereichsbasierte Bereinigungsmethode wie das Python with Schlüsselwort, um damit umzugehen.

Das Hauptproblem ist, dass der Garbage Collector dafür verantwortlich ist, Objekte zu säubern und zu zerstören. In C ++ wird ein Objekt zerstört, wenn es den Gültigkeitsbereich verlässt, also können Sie RAII verwenden und haben eine wohldefinierte Semantik. In Python geht das Objekt außer Reichweite und lebt weiter, solange der GC es mag. Abhängig von Ihrer Python-Implementierung kann dies unterschiedlich sein. CPython mit seinem refcounting-basierten GC ist eher gutartig (so dass Sie selten Probleme sehen), während PyPy, IronPython und Jython ein Objekt sehr lange am Leben erhalten können.

Zum Beispiel:

%Vor%

bad_code gibt ein Dateihandle aus. In CPython spielt es keine Rolle. Der Refcount fällt auf Null und wird sofort gelöscht. In PyPy oder IronPython erhalten Sie möglicherweise IOErrors oder ähnliche Probleme, da Sie alle verfügbaren Dateideskriptoren (bis zu ulimit auf Unix- oder 509-Handles unter Windows) erschöpfen.

Die scope-basierte Bereinigung mit einem Kontextmanager und with ist vorzuziehen, wenn Sie die Bereinigung garantieren müssen. Sie wissen genau, wann Ihre Objekte finalisiert werden. Aber manchmal können Sie diese Art von Scoped Cleanup nicht einfach erzwingen. Das ist der Zeitpunkt, an dem Sie __del__ , atexit oder ähnliche Konstrukte verwenden könnten, um die Säuberung zu optimieren. Es ist nicht zuverlässig, aber besser als nichts.

Sie können Ihre Benutzer entweder mit expliziter Bereinigung belasten oder explizite Bereiche erzwingen, oder Sie können mit __del__ spielen und ab und an einige Kuriositäten sehen (besonders Interpreter Shutdown).

    
schlenk 18.02.2016, 18:16
quelle
2

Es gibt ein paar Probleme mit der Verwendung von __del__ zum Ausführen von Code.

Zum einen funktioniert es nur, wenn Sie Verweise aktiv verfolgen, und selbst dann gibt es keine Garantie dafür, dass es sofort ausgeführt wird, es sei denn, Sie führen im gesamten Code manuell Garbage Collections durch. Ich weiß nicht, wie es Ihnen geht, aber die automatische Speicherbereinigung hat mich ziemlich verwöhnt, wenn es darum geht, Referenzen genau zu verfolgen. Und selbst wenn Sie in Ihrem Code sehr gewissenhaft sind, verlassen Sie sich auch auf andere Benutzer , die Ihren Code genauso sorgfältig verwenden, wenn es um Referenzzählungen geht.

Zweitens gibt es viele Instanzen, in denen __del__ niemals ausgeführt wird. Gab es eine Ausnahme, während Objekte initialisiert und erstellt wurden? Ist der Dolmetscher gegangen? Gibt es irgendwo einen Zirkel? Ja, viele, die hier schief gehen könnten und sehr wenige Wege, sauber und konsequent damit umzugehen.

Drittens, selbst wenn es ausgeführt wird, werden keine Ausnahmen ausgelöst, sodass Sie keine Ausnahmen von ihnen behandeln können, wie Sie es mit anderem Code können. Es ist auch fast unmöglich zu garantieren, dass die __del__ -Methoden von verschiedenen Objekten in einer bestimmten Reihenfolge laufen. Der gebräuchlichste Anwendungsfall für Destruktoren - das Aufräumen und Löschen einer Menge von Objekten - ist daher sinnlos und wird wahrscheinlich nicht wie geplant funktionieren.

Wenn Sie wirklich Code ausführen möchten, gibt es viel bessere Mechanismen - Kontextmanager, Signale / Slots, Ereignisse, etc.

    
Brendan Abel 18.02.2016 18:12
quelle

Tags und Links