579 Stimmen

Erklärungen zu Pythons '__enter__' und '__exit__'

Ich habe dies im Code von jemandem gesehen. Was bedeutet das?

    def __enter__(self):
        return self

    def __exit__(self, type, value, tb):
        self.stream.close()

from __future__ import with_statement#for python2.5 

class a(object):
    def __enter__(self):
        print 'sss'
        return 'sss111'
    def __exit__(self ,type, value, traceback):
        print 'ok'
        return False

with a() as s:
    print s

print s

654voto

ChristopheD Punkte 106139

Mit diesen magischen Methoden ( __enter__ , __exit__ ) ermöglicht es Ihnen, Objekte zu implementieren, die einfach mit dem with Erklärung.

Die Idee dahinter ist, dass es einfach ist, Code zu erstellen, der ein wenig 'cleandown' Code benötigt (stellen Sie sich das als eine try-finally Block). Hier einige weitere Erläuterungen .

Ein nützliches Beispiel könnte ein Datenbank-Verbindungsobjekt sein (das dann automatisch die Verbindung schließt, sobald die entsprechende 'with'-Anweisung den Anwendungsbereich verlässt):

class DatabaseConnection(object):

    def __enter__(self):
        # make a database connection and return it
        ...
        return self.dbconn

    def __exit__(self, exc_type, exc_val, exc_tb):
        # make sure the dbconnection gets closed
        self.dbconn.close()
        ...

Wie oben erläutert, verwenden Sie dieses Objekt mit dem with Anweisung (möglicherweise müssen Sie die from __future__ import with_statement am Anfang der Datei, wenn Sie mit Python 2.5 arbeiten).

with DatabaseConnection() as mydbconn:
    # do stuff

PEP343 -- Die Anweisung 'mit''. hat auch einen schönen Bericht.

140voto

N Randhawa Punkte 7493

Wenn Sie wissen, was Kontextmanager sind, dann brauchen Sie nichts mehr zu verstehen __enter__ y __exit__ magische Methoden. Sehen wir uns ein sehr einfaches Beispiel an.

In diesem Beispiel öffne ich die meineDatei.txt Datei mit Hilfe von öffnen Funktion. Die Website Versuch/Endgültig Block stellt sicher, dass auch beim Auftreten einer unerwarteten Ausnahme meineDatei.txt wird geschlossen.

fp=open(r"C:\Users\SharpEl\Desktop\myfile.txt")
try:
    for line in fp:
        print(line)
finally:
    fp.close()

Jetzt öffne ich dieselbe Datei mit mit Erklärung:

with open(r"C:\Users\SharpEl\Desktop\myfile.txt") as fp:
    for line in fp:
        print(line) 

Wenn Sie sich den Code ansehen, habe ich die Datei nicht geschlossen und es gibt keine Versuch/Endgültig Block. Weil mit Anweisung wird automatisch geschlossen meineDatei.txt . Sie können es sogar überprüfen, indem Sie print(fp.closed) Attribut - das Folgendes zurückgibt True .

Das liegt daran, dass die Dateiobjekte (fp in meinem Beispiel), die von öffnen Funktion hat zwei eingebaute Methoden __enter__ y __exit__ . Er wird auch als Kontextmanager bezeichnet. __enter__ Methode wird am Anfang von mit Block und __exit__ Methode wird am Ende aufgerufen.

Nota: mit Anweisung funktioniert nur mit Objekten, die das Kontextverwaltungsprotokoll unterstützen (d.h. sie haben __enter__ y __exit__ Methoden). Eine Klasse, die beide Methoden implementiert, wird als Kontextmanager-Klasse bezeichnet.

Definieren wir nun unsere eigene Kontext-Manager Klasse.

 class Log:
    def __init__(self,filename):
        self.filename=filename
        self.fp=None    
    def logging(self,text):
        self.fp.write(text+'\n')
    def __enter__(self):
        print("__enter__")
        self.fp=open(self.filename,"a+")
        return self    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("__exit__")
        self.fp.close()

with Log(r"C:\Users\SharpEl\Desktop\myfile.txt") as logfile:
    print("Main")
    logfile.logging("Test1")
    logfile.logging("Test2")

Ich hoffe, Sie haben jetzt ein grundlegendes Verständnis von beidem __enter__ y __exit__ magische Methoden.

100voto

Anentropic Punkte 28341

Ich fand es seltsamerweise schwierig, die Python-Dokumente für __enter__ y __exit__ Methoden durch Googeln zu finden. Um anderen zu helfen, hier der Link:

https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers
(Die Details sind bei beiden Versionen gleich)

object.__enter__(self)
Geben Sie den Laufzeitkontext für dieses Objekt ein. Der with Anweisung wird der Rückgabewert dieser Methode an das/die in der as-Klausel der Anweisung angegebene(n) Ziel(e) gebunden, sofern vorhanden.

object.__exit__(self, exc_type, exc_value, traceback)
Beendet den Laufzeitkontext für dieses Objekt. Die Parameter beschreiben die Ausnahme, die zum Verlassen des Kontextes geführt hat. Wenn der Kontext ohne Ausnahme beendet wurde, sind alle drei Argumente None .

Wenn eine Ausnahme geliefert wird und die Methode die Ausnahme unterdrücken will (d.h. verhindern, dass sie weitergegeben wird), sollte sie einen true-Wert zurückgeben. Andernfalls wird die Ausnahme beim Verlassen der Methode normal verarbeitet.

Beachten Sie, dass __exit__() Methoden sollten die übergebene Ausnahme nicht erneut auslösen; dies liegt in der Verantwortung des Aufrufers.

Ich hatte gehofft, eine klare Beschreibung der __exit__ Argumente der Methode. Diese fehlen zwar, aber wir können sie ableiten...

Vermutlich exc_type ist die Klasse der Ausnahme.

Sie besagt, dass Sie die übergebene Ausnahme nicht noch einmal erhöhen sollten. Dies legt nahe, dass eines der Argumente eine tatsächliche Exception-Instanz sein könnte ... oder vielleicht sollen Sie sie selbst aus dem Typ und dem Wert instanziieren?

Die Antwort finden wir in diesem Artikel:
http://effbot.org/zone/python-with-statement.htm

Zum Beispiel das Folgende __exit__ Methode schluckt jeden TypeError, lässt aber alle anderen Ausnahmen durch:

def __exit__(self, type, value, traceback):
    return isinstance(value, TypeError)

...so eindeutig value ist eine Exception-Instanz.

Und vermutlich traceback ist ein Python Traceback Objekt.

Beachten Sie, dass es hier weitere Dokumente gibt:
https://docs.python.org/3/library/stdtypes.html#context-manager-types

...finden Sie eine etwas ausführlichere Erklärung der __enter__ y __exit__ Methoden. Insbesondere ist es deutlicher, dass __exit__ sollte einen booleschen Wert zurückgeben (obwohl truthy oder falsy auch gut funktionieren, z.B. eine implizite None zurückgeben, wird die Ausnahme standardmäßig weitergegeben).

97voto

Yuri Feldman Punkte 1952

Zusätzlich zu den obigen Antworten zur Veranschaulichung der Aufrufreihenfolge ein einfaches Laufbeispiel

class myclass:
    def __init__(self):
        print("__init__")

    def __enter__(self): 
        print("__enter__")

    def __exit__(self, type, value, traceback):
        print("__exit__")

    def __del__(self):
        print("__del__")

with myclass(): 
    print("body")

Erzeugt die Ausgabe:

__init__
__enter__
body
__exit__
__del__

Zur Erinnerung: Bei Verwendung der Syntax with myclass() as mc erhält die Variable mc den Wert, der von __enter__() in dem oben genannten Fall None ! Für eine solche Verwendung muss ein Rückgabewert definiert werden, wie z.B.:

def __enter__(self): 
    print('__enter__')
    return self

7voto

Wira Bhakti Punkte 372

Versuchen Sie, meine Antworten hinzuzufügen (mein Gedanke des Lernens) :

__enter__ y [__exit__] sind beides Methoden, die beim Eintritt in und Austritt aus dem Body von "" aufgerufen werden. die mit-Anweisung " ( PEP 343 ) und die Implementierung von beiden wird als Kontextmanager bezeichnet.

die with-Anweisung soll die Flusskontrolle der try- finally-Klausel verbergen und den Code undurchschaubar machen.

Die Syntax der with-Anweisung lautet :

with EXPR as VAR:
    BLOCK

die (wie in PEP 343 erwähnt) zu übersetzen sind:

mgr = (EXPR)
exit = type(mgr).__exit__  # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
    try:
        VAR = value  # Only if "as VAR" is present
        BLOCK
    except:
        # The exceptional case is handled here
        exc = False
        if not exit(mgr, *sys.exc_info()):
            raise
        # The exception is swallowed if exit() returns true
finally:
    # The normal and non-local-goto cases are handled here
    if exc:
        exit(mgr, None, None, None)

Probieren Sie einen Code aus:

>>> import logging
>>> import socket
>>> import sys

#server socket on another terminal / python interpreter
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.listen(5)
>>> s.bind((socket.gethostname(), 999))
>>> while True:
>>>    (clientsocket, addr) = s.accept()
>>>    print('get connection from %r' % addr[0])
>>>    msg = clientsocket.recv(1024)
>>>    print('received %r' % msg)
>>>    clientsocket.send(b'connected')
>>>    continue

#the client side
>>> class MyConnectionManager:
>>>     def __init__(self, sock, addrs):
>>>         logging.basicConfig(level=logging.DEBUG, format='%(asctime)s \
>>>         : %(levelname)s --> %(message)s')
>>>         logging.info('Initiating My connection')
>>>         self.sock = sock
>>>         self.addrs = addrs
>>>     def __enter__(self):
>>>         try:
>>>             self.sock.connect(addrs)
>>>             logging.info('connection success')
>>>             return self.sock
>>>         except:
>>>             logging.warning('Connection refused')
>>>             raise
>>>     def __exit__(self, type, value, tb):
>>>             logging.info('CM suppress exception')
>>>             return False
>>> addrs = (socket.gethostname())
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> with MyConnectionManager(s, addrs) as CM:
>>>     try:
>>>         CM.send(b'establishing connection')
>>>         msg = CM.recv(1024)
>>>         print(msg)
>>>     except:
>>>         raise
#will result (client side) :
2018-12-18 14:44:05,863         : INFO --> Initiating My connection
2018-12-18 14:44:05,863         : INFO --> connection success
b'connected'
2018-12-18 14:44:05,864         : INFO --> CM suppress exception

#result of server side
get connection from '127.0.0.1'
received b'establishing connection'

und versuchen Sie es nun manuell (nach der Syntax von translate):

>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #make new socket object
>>> mgr = MyConnection(s, addrs)
2018-12-18 14:53:19,331         : INFO --> Initiating My connection
>>> ext = mgr.__exit__
>>> value = mgr.__enter__()
2018-12-18 14:55:55,491         : INFO --> connection success
>>> exc = True
>>> try:
>>>     try:
>>>         VAR = value
>>>         VAR.send(b'establishing connection')
>>>         msg = VAR.recv(1024)
>>>         print(msg)
>>>     except:
>>>         exc = False
>>>         if not ext(*sys.exc_info()):
>>>             raise
>>> finally:
>>>     if exc:
>>>         ext(None, None, None)
#the result:
b'connected'
2018-12-18 15:01:54,208         : INFO --> CM suppress exception

das Ergebnis auf der Serverseite ist dasselbe wie zuvor

Entschuldigung für mein schlechtes Englisch und meine unklaren Erklärungen, danke....

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