Implementierung der Verwendung von 'with object () as f' in einer benutzerdefinierten Klasse in Python

77

Ich muss ein dateiähnliches Objekt in Python öffnen (es ist eine serielle Verbindung über / dev /) und es dann schließen. Dies geschieht mehrmals in verschiedenen Methoden meiner Klasse. Ich habe die Datei im Konstruktor geöffnet und dann im Destruktor geschlossen. Ich bekomme allerdings seltsame Fehler und ich denke, das hat mit dem Garbage Collector zu tun. Ich bin es immer noch nicht gewohnt, nicht genau zu wissen, wann meine Objekte gelöscht werden = \

Der Grund, warum ich das getan habe, ist, dass ich tcsetattrjedes Mal, wenn ich es öffne, eine Reihe von Parametern verwenden muss und es nervig wird, all das überall zu tun. Ich möchte also eine innere Klasse implementieren, um all das zu handhaben, damit ich sie verwenden kann
with Meter('/dev/ttyS2') as m:

Ich habe online gesucht und konnte keine wirklich gute Antwort darauf finden, wie die withSyntax implementiert ist. Ich habe gesehen, dass es die __enter__(self)und __exit(self)__Methoden verwendet. Aber ist alles, was ich tun muss, um diese Methoden zu implementieren, und ich kann die mit Syntax verwenden? Oder steckt noch mehr dahinter?

Gibt es entweder ein Beispiel dafür oder eine Dokumentation darüber, wie es bereits auf Dateiobjekten implementiert ist, die ich mir ansehen kann?

Falmarri
quelle

Antworten:

100

Diese Methoden sind so ziemlich alles, was Sie brauchen, damit das Objekt mit withAnweisung funktioniert .

In müssen __enter__Sie das Dateiobjekt zurückgeben, nachdem Sie es geöffnet und eingerichtet haben.

In müssen __exit__Sie das Dateiobjekt schließen. Der Code zum Schreiben befindet sich im withAnweisungshauptteil.

class Meter():
    def __init__(self, dev):
        self.dev = dev
    def __enter__(self):
        #ttysetattr etc goes here before opening and returning the file object
        self.fd = open(self.dev, MODE)
        return self.fd
    def __exit__(self, type, value, traceback):
        #Exception handling here
        close(self.fd)

meter = Meter('dev/tty0')
with meter as m:
    #here you work with the file object.
    m.read()
dekomote
quelle
22
def __enter__(self): return selfWenn Sie einen Verweis auf Meterden with-Block wünschen .
Morgoth
37

Am einfachsten ist es, die Kontextlib des Standard-Python-Bibliotheksmoduls zu verwenden :

import contextlib

@contextlib.contextmanager
def themeter(name):
    theobj = Meter(name)
    yield theobj
    theobj.close()  # or whatever you need to do at exit

Dies macht sich nicht zu Metereinem Kontextmanager (und ist daher für diese Klasse nicht invasiv), sondern "dekoriert" sie (nicht im Sinne von Pythons "Dekoratorsyntax", sondern fast, aber nicht ganz im Sinne des Dekorateur Entwurfsmuster ;-) mit einer Fabrik Funktion , themeterdie ist ein Kontext - Manager (das ist der contextlib.contextmanagerDekorateur aus dem „Single - Builds yield“ Generatorfunktion Sie schreiben) - das macht es so viel einfacher , das Ein- und Aussteigen Zustand zu trennen, vermeidet Verschachtelung & c.

Alex Martelli
quelle
1
Dies ist viel einfacher als der klassenbasierte Ansatz.
Byxor
6
Um sicherzustellen, dass theobj.close()es auch im Ausnahmefall ausgeführt wird, können Sie yield theobjmit einem try...finallyBlock umbrechen.
Akesfeden
1

Der erste Google-Hit (für mich) erklärt es einfach genug:

http://effbot.org/zone/python-with-statement.htm

und das PEP erklärt es genauer (aber auch ausführlicher):

http://www.python.org/dev/peps/pep-0343/

Glenn Maynard
quelle
2
Ich habe diese tatsächlich gesehen und dachte nicht, dass einer von ihnen überhaupt sehr klar war. Der erste sagt ziemlich genau "Es gibt Methoden, die _ enter_ und _ exit_ genannt werden " und erklärt wenig bis gar nichts über sie. Und der PEP spricht so ziemlich nur über die Notwendigkeit der neuen Syntax
Falmarri
Nein, es ist einfach mit Beispielen und Erklärungen. Sie müssen es erneut lesen. Dies ist kein komplexes Merkmal.
Glenn Maynard
5
Unabhängig davon sollten Sie wissen, dass Nur-Link-Antworten von der Community insgesamt schlecht aufgenommen werden. Teilen Sie zumindest etwas aus der Quelle mit, das Ihre Behauptungen unterstützt.
IAbstract