class Package:
def __init__(self):
self.files = []
# ...
def __del__(self):
for file in self.files:
os.unlink(file)
__del__(self)
oben schlägt mit einer AttributeError-Ausnahme fehl. Ich verstehe, dass Python beim Aufrufen nicht die Existenz von "globalen Variablen" (Mitgliedsdaten in diesem Kontext?) Garantiert__del__()
. Wenn dies der Fall ist und dies der Grund für die Ausnahme ist, wie stelle ich sicher, dass das Objekt ordnungsgemäß zerstört wird?
python
destructor
wilhelmtell
quelle
quelle
__del__
nicht als Gegenstück zu verwendet werden sollte__init__
. (Dh es ist kein "Destruktor" im Sinne__init__
eines Konstruktors.Antworten:
Ich würde empfehlen, die Python-
with
Anweisung zum Verwalten von Ressourcen zu verwenden, die bereinigt werden müssen. Das Problem bei der Verwendung einer explizitenclose()
Anweisung besteht darin, dass Sie sich Sorgen machen müssen, dass Personen vergessen, sie überhaupt aufzurufen oder sie in einemfinally
Block zu platzieren, um ein Ressourcenleck zu verhindern, wenn eine Ausnahme auftritt.with
Erstellen Sie eine Klasse mit den folgenden Methoden, um die Anweisung zu verwenden:In Ihrem obigen Beispiel würden Sie verwenden
Wenn dann jemand Ihre Klasse benutzen wollte, würde er Folgendes tun:
Die Variable package_obj ist eine Instanz vom Typ Package (dies ist der von der
__enter__
Methode zurückgegebene Wert ). Die__exit__
Methode wird automatisch aufgerufen, unabhängig davon, ob eine Ausnahme auftritt oder nicht.Sie könnten diesen Ansatz sogar noch einen Schritt weiter gehen. Im obigen Beispiel könnte jemand Package weiterhin mit seinem Konstruktor instanziieren, ohne die
with
Klausel zu verwenden. Du willst nicht, dass das passiert. Sie können dies beheben, indem Sie eine PackageResource-Klasse erstellen, die die Methoden__enter__
und definiert__exit__
. Dann würde die Package-Klasse streng innerhalb der__enter__
Methode definiert und zurückgegeben. Auf diese Weise konnte der Aufrufer die Package-Klasse niemals instanziieren, ohne einewith
Anweisung zu verwenden:Sie würden dies wie folgt verwenden:
quelle
with Resource(param1, param2) as r: # ...
Die Standardmethode ist
atexit.register
:Beachten Sie jedoch, dass alle erstellten Instanzen so lange bestehen bleiben,
Package
bis Python beendet wird.Demo mit dem oben als package.py gespeicherten Code :
quelle
with
? Haben sie explizit aufgerufen__enter__
?). Der Nachteil ist natürlich, wenn die Bereinigung vor Python erfolgen muss Ausgänge, es wird nicht funktionieren. In meinem Fall ist es mir egal, ob das Objekt den Gültigkeitsbereich verlässt oder erst, wenn Python beendet wird. :)atexit.register(self.__exit__)
?__exit__
und einen Kontextmanager verwenden? Auch__exit__
nimmt zusätzliche Argumente (dh__exit__(self, type, value, traceback)
), so müssen Sie würde für diejenigen accout. In jedem Fall sollten Sie eine separate Frage zu SO stellen, da Ihr Anwendungsfall ungewöhnlich erscheint.Als Anhang zu Clint Antwort , können Sie vereinfachen
PackageResource
mitcontextlib.contextmanager
:Alternativ, obwohl wahrscheinlich nicht als Pythonic, können Sie Folgendes überschreiben
Package.__new__
:und einfach benutzen
with Package(...) as package
.Um die Dinge kürzer zu machen, benennen Sie Ihre Bereinigungsfunktion
close
und verwenden Sie siecontextlib.closing
. In diesem Fall können Sie entweder die unverändertePackage
Klasse über verwendenwith contextlib.closing(Package(...))
oder__new__
die einfachere überschreibenUnd dieser Konstruktor wird geerbt, so dass Sie einfach erben können, z
quelle
Package.__new__()
Methode nicht umgehen können . Oder vielleicht können wir. Wir könnten wahrscheinlich entweder einen Klassendekorateur oder eine Metaklasse definieren, die dieses Boilerplate für uns generisiert. Nahrung für pythonische Gedanken.Package
sollte dies auch tun (obwohl ich das noch nicht getestet habe), daher sollte keine Metaklasse erforderlich sein. Obwohl ich habe ein paar ziemlich merkwürdige Weise zu nutzen metaclasses in der Vergangenheit ... gefundenPackage
(oder besser eine Klasse mit dem NamenClosing
) als übergeordnetes Element verwenden könnenobject
. Aber fragen Sie mich nicht, wie die mehrfache Vererbung dies durcheinander bringt ...Ich denke nicht, dass es möglich ist, dass beispielsweise Mitglieder entfernt werden, bevor sie
__del__
aufgerufen werden. Ich vermute, dass der Grund für Ihren speziellen AttributeError woanders liegt (vielleicht entfernen Sie self.file fälschlicherweise woanders).Wie die anderen betonten, sollten Sie jedoch die Verwendung vermeiden
__del__
. Der Hauptgrund dafür ist, dass Instanzen mit__del__
nicht mit Müll gesammelt werden (sie werden erst freigegeben, wenn ihr Refcount 0 erreicht). Wenn Ihre Instanzen an Zirkelverweisen beteiligt sind, bleiben sie daher so lange im Speicher, wie die Anwendung ausgeführt wird. (Ich kann mich bei all dem irren, ich müsste die gc-Dokumente noch einmal lesen, aber ich bin mir ziemlich sicher, dass es so funktioniert).quelle
__del__
können durch Müll gesammelt werden, wenn ihre Referenzanzahl von anderen Objekten mit__del__
Null ist und sie nicht erreichbar sind. Dies bedeutet, wenn Sie einen Referenzzyklus zwischen Objekten mit haben__del__
, wird keiner von diesen erfasst. Jeder andere Fall sollte jedoch wie erwartet gelöst werden.Eine bessere Alternative ist die Verwendung von schwachref.finalize . Siehe die Beispiele unter Finalizer-Objekte und Vergleichen von Finalisierern mit __del __ () -Methoden .
quelle
stop()
Methode, um die Ports undjoin()
die Prozesse zu schließen. Wenn das Programm jedoch unerwartet beendet wird,stop()
wird es nicht aufgerufen - das habe ich mit einem Finalizer gelöst. Aber auf jeden Fall rufe ich_finalizer.detach()
die stop-Methode auf, um zu verhindern, dass sie zweimal aufgerufen wird (manuell und später erneut vom Finalizer).Ich denke, das Problem könnte darin liegen,
__init__
dass mehr Code als gezeigt vorhanden ist.__del__
wird auch dann aufgerufen, wenn__init__
es nicht richtig ausgeführt wurde oder eine Ausnahme ausgelöst hat.Quelle
quelle
__del__
besteht darin, alle Mitglieder auf Klassenebene explizit zu deklarieren und sicherzustellen, dass sie immer vorhanden sind, auch wenn dies__init__
fehlschlägt. In dem gegebenen Beispielfiles = ()
würde funktionieren, obwohl Sie meistens nur zuweisen würdenNone
; In beiden Fällen müssen Sie noch den tatsächlichen Wert in zuweisen__init__
.Hier ist ein minimales Arbeitsskelett:
Wichtig: kehre selbst zurück
Wenn Sie wie ich sind und den
return self
Teil (von Clint Millers korrekter Antwort ) übersehen , werden Sie auf diesen Unsinn starren:Hoffe es hilft der nächsten Person.
quelle
Schließen Sie Ihren Destruktor einfach mit einer try / exception-Anweisung ein, und es wird keine Ausnahme ausgelöst, wenn Ihre Globals bereits entsorgt sind.
Bearbeiten
Versuche dies:
Die Dateiliste wird in die del- Funktion eingefügt, die zum Zeitpunkt des Aufrufs garantiert vorhanden ist. Der schwache Ref-Proxy soll verhindern, dass Python oder Sie selbst die Variable self.files irgendwie löschen (wenn sie gelöscht wird, hat dies keine Auswirkungen auf die ursprüngliche Dateiliste). Wenn dies nicht der Fall ist, wird dies gelöscht, obwohl mehr Verweise auf die Variable vorhanden sind, können Sie die Proxy-Kapselung entfernen.
quelle
Es scheint, dass der idiomatische Weg, dies zu tun, darin besteht, eine
close()
Methode (oder eine ähnliche) bereitzustellen und sie explizit aufzurufen.quelle