599 Stimmen

Wie bereinige ich ein Python-Objekt richtig?

class Package:
    def __init__(self):
        self.files = []

    # ...

    def __del__(self):
        for file in self.files:
            os.unlink(file)

__del__(self) schlägt mit einer AttributeError-Ausnahme fehl. Ich verstehe Python garantiert nicht das Vorhandensein von "globalen Variablen" (Mitgliedsdaten in diesem Zusammenhang?), wenn __del__() aufgerufen wird. Wenn das der Fall ist und dies der Grund für die Ausnahme ist, wie kann ich sicherstellen, dass das Objekt ordnungsgemäß zerstört wird?

3 Stimmen

Wenn ich lese, was du verlinkt hast, scheinen globale Variablen, die verschwinden, hier nicht zu gelten, es sei denn, du sprichst davon, dass dein Programm beendet wird, wobei ich vermute, dass es nach dem, was du verlinkt hast, MÖGLICH sein könnte, dass das Betriebssystemmodul selbst bereits verschwunden ist. Ansonsten glaube ich nicht, dass es auf Mitgliedsvariablen in einer __del__()-Methode zutrifft.

3 Stimmen

Die Ausnahme wird ausgelöst, lange bevor mein Programm beendet wird. Die AttributeError-Ausnahme, die ich erhalte, ist Python, das sagt, dass es self.files nicht als ein Attribut von Package erkennt. Vielleicht verstehe ich das falsch, aber wenn sie mit "globals" keine Variablen meinen, die global für Methoden sind (sondern möglicherweise lokal für die Klasse), dann weiß ich nicht, was diese Ausnahme verursacht. Google weist darauf hin, dass Python sich das Recht vorbehält, Mitgliedsdaten zu bereinigen, bevor __del__(self) aufgerufen wird.

1 Stimmen

Der Code wie gepostet scheint für mich zu funktionieren (mit Python 2.5). Können Sie den tatsächlichen Code posten, der fehlschlägt - oder eine vereinfachte (je einfacher, desto besser) Version, die immer noch den Fehler verursacht?

18voto

Virgil Dupras Punkte 2594

Ich glaube nicht, dass es möglich ist, dass Mitglieder einer Instanz entfernt werden können, bevor __del__ genannt wird. Meine Vermutung wäre, dass der Grund für Ihren speziellen AttributeError woanders liegt (vielleicht haben Sie self.file versehentlich an anderer Stelle entfernt).

Wie bereits erwähnt, sollten Sie jedoch die Verwendung von __del__ . Der Hauptgrund dafür ist, dass Instanzen mit __del__ werden nicht entsorgt (sie werden erst freigegeben, wenn ihr Refcount 0 erreicht). Wenn Ihre Instanzen also in zirkuläre Referenzen verwickelt sind, werden sie so lange im Speicher bleiben, wie die Anwendung läuft. (Es kann sein, dass ich mich in diesem Punkt irre, ich müsste die gc-Dokumente noch einmal lesen, aber ich bin mir ziemlich sicher, dass es so funktioniert).

6 Stimmen

Objekte mit __del__ können garbage collected werden, wenn ihre Referenzanzahl von anderen Objekten mit __del__ Null ist und sie nicht erreichbar sind. Das heißt, wenn Sie einen Referenzzyklus zwischen Objekten mit __del__ wird nichts davon eingezogen. Alle anderen Fälle sollten jedoch wie erwartet gelöst werden.

5 Stimmen

"Ab Python 3.4 verhindern __del__()-Methoden nicht mehr, dass Referenzzyklen garbage collected werden, und Modulglobale werden beim Herunterfahren des Interpreters nicht mehr zu None gezwungen. Daher sollte dieser Code ohne Probleme auf CPython funktionieren." - docs.python.org/3.6/Bibliothek/

15voto

n3o59hf Punkte 469

Ich denke, das Problem könnte darin liegen __init__ wenn es mehr Code gibt als angegeben?

__del__ wird auch dann aufgerufen, wenn __init__ nicht ordnungsgemäß ausgeführt wurde oder eine Ausnahme ausgelöst hat.

Quelle

2 Stimmen

Das klingt sehr wahrscheinlich. Der beste Weg, dieses Problem zu vermeiden, wenn Sie __del__ ist es, alle Mitglieder explizit auf Klassenebene zu deklarieren, um sicherzustellen, dass sie immer existieren, auch wenn __init__ scheitert. Im gegebenen Beispiel, files = () funktionieren würde, obwohl man meist nur die None ; in beiden Fällen müssen Sie immer noch den tatsächlichen Wert in __init__ .

9voto

Unknown Punkte 44574

Wickeln Sie Ihren Destruktor einfach mit einer try/except-Anweisung ein, und es wird keine Ausnahme ausgelöst, wenn Ihre Globals bereits entsorgt sind.

Editar

Versuchen Sie dies:

from weakref import proxy

class MyList(list): pass

class Package:
    def __init__(self):
        self.__del__.im_func.files = MyList([1,2,3,4])
        self.files = proxy(self.__del__.im_func.files)

    def __del__(self):
        print self.__del__.im_func.files

Es wird die Dateiliste in die del Funktion, deren Existenz zum Zeitpunkt des Aufrufs garantiert ist. Der weakref-Proxy soll verhindern, dass Python oder Sie selbst die Variable self.files irgendwie löschen (wenn sie gelöscht wird, hat das keine Auswirkungen auf die ursprüngliche Dateiliste). Wenn es nicht der Fall ist, dass die Variable gelöscht wird, obwohl es weitere Verweise auf die Variable gibt, können Sie die Proxy-Kapselung entfernen.

2 Stimmen

Das Problem ist, dass es für mich zu spät ist, wenn die Mitgliederdaten verschwunden sind. Ich brauche diese Daten. Siehe meinen Code oben: Ich brauche die Dateinamen, um zu wissen, welche Dateien ich entfernen muss. Ich habe meinen Code jedoch vereinfacht, da es andere Daten gibt, die ich selbst bereinigen muss (d. h. der Interpreter weiß nicht, wie er sie bereinigen soll).

6voto

Bastien Léonard Punkte 57728

Es scheint, dass die idiomatische Art und Weise, dies zu tun, darin besteht, eine close() Methode (oder ähnlich) und rufen Sie sie explizit auf.

24 Stimmen

Diesen Ansatz habe ich früher verwendet, aber ich bin dabei auf andere Probleme gestoßen. Da überall Ausnahmen von anderen Bibliotheken ausgelöst werden, brauche ich die Hilfe von Python, um das Chaos im Falle eines Fehlers zu beseitigen. Insbesondere brauche ich Python, um den Destruktor für mich aufzurufen, denn sonst wird der Code schnell unübersichtlich, und ich werde sicher einen Exit-Point vergessen, wo ein Aufruf von .close() sein sollte.

3voto

ViFI Punkte 895

atexit.register ist der Standardweg, wie bereits in ostrakach's Antwort .

Es ist jedoch zu beachten, dass die Reihenfolge, in der die Objekte gelöscht werden, nicht verlässlich ist, wie das folgende Beispiel zeigt.

import atexit

class A(object):

    def __init__(self, val):
        self.val = val
        atexit.register(self.hello)

    def hello(self):
        print(self.val)

def hello2():
    a = A(10)

hello2()    
a = A(20)

Hier scheint die umgekehrte Reihenfolge, in der die Objekte erstellt wurden, legitim zu sein, da das Programm die folgende Ausgabe liefert:

20
10

Allerdings, wenn in einem größeren Programm, Python's Garbage Collection tritt in Objekt, das aus der Lebensdauer ist, würde zuerst zerstört werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X