Frage

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

    # ...

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

__del__(self) nicht oben mit einer Attribut Ausnahme. Ich verstehe Python ist keine Garantie die Existenz von „globalen Variablen“ (Mitgliederdaten in diesem Zusammenhang?), wenn __del__() aufgerufen wird. Wenn das der Fall ist, und dies ist der Grund für die Ausnahme, wie kann ich sicher, dass das Objekt selbst zerstört richtig?

War es hilfreich?

Lösung

würde ich empfehlen, für die Verwaltung von Ressourcen Pythons with Anweisung, die bis zu gereinigt werden muß. Das Problem mit einer expliziten close() Aussage ist, dass Sie über die Menschen vergessen zu kümmern, nennt es überhaupt oder zu vergessen, es in einem finally Block zu platzieren, eine Ressource Leck zu verhindern, wenn eine Ausnahme auftritt.

die with Anweisung verwenden, erstellen Sie eine Klasse mit den folgenden Methoden:

  def __enter__(self)
  def __exit__(self, exc_type, exc_value, traceback)

In Ihrem Beispiel oben, dann würden Sie verwenden

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

    def __enter__(self):
        return self

    # ...

    def __exit__(self, exc_type, exc_value, traceback):
        for file in self.files:
            os.unlink(file)

Wenn dann jemand benutzen, um Ihre Klasse will, sie würden wie folgt vor:

with Package() as package_obj:
    # use package_obj

Die Variable package_obj wird eine Instanz vom Typ Paket sein (es ist der Wert durch die __enter__ Methode zurückgegeben). Seine __exit__ Methode wird automatisch aufgerufen werden, unabhängig davon, ob eine Ausnahme auftritt.

Man könnte sogar nimmt diesen Ansatz einen Schritt weiter. In dem obigen Beispiel könnte jemand noch instanziiert Paket seinen Konstruktor ohne die with-Klausel. Sie wollen nicht, dass das passiert. Sie können dieses Problem beheben, indem Sie eine PackageResource Klasse erstellen, die die __enter__ und __exit__ Methoden definiert. Dann würde die Paketklasse streng innerhalb der __enter__ Methode und zurück definiert werden. Auf diese Weise der Anrufer nie ohne Verwendung einer with Anweisung, um die Paketklasse instanziiert könnte:

class PackageResource:
    def __enter__(self):
        class Package:
            ...
        self.package_obj = Package()
        return self.package_obj

    def __exit__(self, exc_type, exc_value, traceback):
        self.package_obj.cleanup()

Sie würden diese wie folgt verwenden:

with PackageResource() as package_obj:
    # use package_obj

Andere Tipps

Die Standardmethode ist die Verwendung atexit.register :

# package.py
import atexit
import os

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

    def cleanup(self):
        print("Running cleanup...")
        for file in self.files:
            print("Unlinking file: {}".format(file))
            # os.unlink(file)

Aber Sie sollten bedenken, dass dies alle erstellten Instanzen von Package bestehen bleiben, bis Python beendet wird.

Demo mit dem Code oben gespeichert als package.py :

$ python
>>> from package import *
>>> p = Package()
>>> q = Package()
>>> q.files = ['a', 'b', 'c']
>>> quit()
Running cleanup...
Unlinking file: a
Unlinking file: b
Unlinking file: c
Running cleanup...

Als Anhang zum Clint Antwort , können Sie PackageResource mit contextlib.contextmanager :

@contextlib.contextmanager
def packageResource():
    class Package:
        ...
    package = Package()
    yield package
    package.cleanup()

Alternativ, wenn auch wahrscheinlich nicht so Pythonic Sie können Package.__new__ außer Kraft setzen:

class Package(object):
    def __new__(cls, *args, **kwargs):
        @contextlib.contextmanager
        def packageResource():
            # adapt arguments if superclass takes some!
            package = super(Package, cls).__new__(cls)
            package.__init__(*args, **kwargs)
            yield package
            package.cleanup()

    def __init__(self, *args, **kwargs):
        ...

und einfach verwenden with Package(...) as package.

Um die Dinge kürzer, nennen Sie Ihre Bereinigungsfunktion close und verwenden contextlib.closing , wobei in diesem Fall können Sie entweder die unmodifizierte Package Klasse über with contextlib.closing(Package(...)) verwenden oder seine __new__ der einfacheren

class Package(object):
    def __new__(cls, *args, **kwargs):
        package = super(Package, cls).__new__(cls)
        package.__init__(*args, **kwargs)
        return contextlib.closing(package)

Und dieser Konstruktor vererbt wird, so können Sie einfach erben, z.

class SubPackage(Package):
    def close(self):
        pass

Ich glaube nicht, dass es möglich ist, zum Beispiel Mitglieder entfernt werden, bevor __del__ genannt wird. Meine Vermutung wäre, dass der Grund für Ihre Attribute woanders ist (vielleicht entfernen Sie versehentlich self.file an anderer Stelle).

Da jedoch die anderen darauf hingewiesen wird, sollten Sie die Verwendung __del__ vermeiden. Der Hauptgrund dafür ist, dass Fälle mit __del__ nicht Müll gesammelt werden (sie werden nur freigegeben werden, wenn ihre refcount erreicht 0) gewonnen. Wenn also die Instanzen in zirkulären Referenzen beteiligt sind, werden sie so lange, wie die Anwendung läuft in Erinnerung bleiben. (Ich kann über all diese allerdings irrt, würde ich wieder die gc-Dokumente lesen müssen, aber ich bin ziemlich sicher, dass es so funktioniert).

Ich denke, das Problem in __init__ sein könnte, wenn es mehr Code als gezeigt?

__del__ aufgerufen wird, selbst wenn __init__ wurde nicht richtig ausgeführt oder hat eine Ausnahme.

Just wickeln Sie Ihren destructor mit einem try / except-Anweisung, und es wird keine Ausnahme, wenn Ihr Globals wirft bereits entsorgt werden.

Bearbeiten

Versuchen Sie folgendes:

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 der del Funktion stopfen, die zum Zeitpunkt des Anrufs existieren garantiert. Der WeakRef Proxy ist Python zu verhindern, oder um sich vor dem Löschen des self.files Variable irgendwie (wenn es gelöscht wird, dann wird es nicht die Originaldatei Liste beeinflussen). Wenn es nicht der Fall ist, dass diese auch gelöscht werden, obwohl es mehr Hinweise auf die variabel sind, dann können Sie die Proxy-Verkapselung entfernen.

Hier ist ein minimales Arbeits Skelett:

class SkeletonFixture:

    def __init__(self):
        pass

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        pass

    def method(self):
        pass


with SkeletonFixture() as fixture:
    fixture.method()

Wichtig: Rückkehr Selbst


Wenn Sie wie ich sind, und den return self Teil übersehen (von Clint Miller richtigen Antwort ), werden Sie starre auf diesem Unsinn:

Traceback (most recent call last):
  File "tests/simplestpossible.py", line 17, in <module>                                                                                                                                                          
    fixture.method()                                                                                                                                                                                              
AttributeError: 'NoneType' object has no attribute 'method'

Ich verbrachte einen halben Tag zu diesem Thema. Hoffe, dass es die nächste Person hilft.

Es scheint, dass der idiomatische Weg, dies zu tun, ist eine close() Methode (oder ähnliches) zur Verfügung zu stellen, und nennen Sie es explizit.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top