2009-12-31 8 views
182

Ho visto questo nel codice di qualcuno. Cosa significa?Spiegando Python "__enter__" e "__exit__"

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 
+12

Una buona spiegazione qui: http://effbot.org/ zone/python-with-statement.htm – Manur

+0

@StevenVascellaro La modifica del codice di una domanda è generalmente una cattiva idea, ** specialmente ** quando ci sono errori nel codice. Questa domanda è stata posta con Py2 in mente, e non c'è motivo di aggiornarlo su Py3. – jpaugh

risposta

229

L'utilizzo di questi metodi magici (__enter__, __exit__) consente di implementare gli oggetti che possono essere utilizzati facilmente con la dichiarazione with.

L'idea è che semplifica la creazione di codice che richiede l'esecuzione di un codice di "pulizia" (si pensi al blocco try-finally). Some more explanation here.

Un utile esempio potrebbe essere un oggetto connessione al database (che quindi chiude automagicamente la connessione una volta che il corrispondente 'with'-economico passa nell'ambito):

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() 
     ... 

Come spiegato in precedenza, utilizzare l'oggetto con la Istruzione with (potrebbe essere necessario fare from __future__ import with_statement nella parte superiore del file se si utilizza Python 2.5).

with DatabaseConnection() as mydbconn: 
    # do stuff 

PEP343 -- The 'with' statement' ha una bella recensione pure.

+4

Probabilmente, '__enter__' dovrebbe restituire' self' sempre come allora solo altri metodi della classe possono essere richiamati nel contesto. – ViFI

+0

@ViFI Esistono 4 esempi di 'def __enter __ (self)' in PEP 343 e nessuno esegue il 'return self': https://www.python.org/dev/peps/pep-0343/. Perchè la pensi così? – Grief

+2

@Grief: Per 2 motivi, a mio parere, 1) non sarò in grado di chiamare altri metodi sull'oggetto 'self' come spiegato qui: http://stackoverflow.com/questions/38281853/should-the- valore-di-ritorno-per-metodo-di-immissione-sempre-self-in-python 2) self.XYZ è solo una parte dell'oggetto stesso e l'handle di ritorno solo per quello che mi sembra inappropriato dal punto di vista della manutenzione. Preferirei preferire restituire l'handle per completare l'oggetto e quindi fornire API pubbliche solo agli oggetti 'self' dei componenti, che desidero esporre all'utente come in ' con open (abc.txt, 'r') come fin: content = fin.read() ' – ViFI

29

Se sai cosa sono i gestori di contesto , non hai bisogno di altro per capire i metodi magici __enter__ e __exit__. Vediamo un esempio molto semplice.

In questo esempio sto aprendo miofile.txt con l'aiuto di aperto funzione di. Il blocco try/finally garantisce che anche se si verifica un'eccezione imprevista myfile.txt verrà chiuso.

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

Ora sto aprendo stesso file con con dichiarazione:

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

Se si guarda il codice, non ho chiuso il file & non c'è try/finally blocco . Perché con l'istruzione si chiude automaticamente myfile.txt. Puoi anche controllarlo chiamando l'attributo print(fp.closed) - che restituisce True.

Questo perché gli oggetti file (FP nel mio esempio) restituiti dalla aperto funzione ha due metodi built-in __enter__ e __exit__. È anche noto come gestore del contesto. Il metodo __enter__ viene chiamato all'inizio di con il blocco e il metodo __exit__ viene chiamato alla fine. Nota: con l'istruzione funziona solo con oggetti che supportano il protocollo di mamangement del contesto, ad esempio i metodi __enter__ e __exit__. Una classe che implementa entrambi i metodi è nota come classe di gestione del contesto.

Ora è possibile definire la propria classe gestore contesto.

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") 

spero ora avete conoscenza di base di entrambi i metodi magici __enter__ e __exit__.

16

ho trovato stranamente difficile individuare la documentazione Python per __enter__ e __exit__ metodi da usare Google, in modo da aiutare gli altri ecco il link:

https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers

object.__enter__(self)
immettere il contesto di esecuzione relativo a questo oggetto. L'istruzione with associa il valore restituito di questo metodo alle destinazioni specificate nella clausola as dell'istruzione, se presente.

object.__exit__(self, exc_type, exc_value, traceback)
Uscire dal contesto di runtime relativo a questo oggetto. I parametri descrivono l'eccezione che ha causato l'uscita dal contesto. Se il contesto è stato chiuso senza eccezioni, tutti e tre gli argomenti saranno None.

Se viene fornita un'eccezione e il metodo desidera eliminare l'eccezione (ad esempio, impedirne la propagazione), dovrebbe restituire un valore vero. Altrimenti, l'eccezione verrà elaborata normalmente all'uscita da questo metodo.

Nota che i metodi __exit__() non devono controrilanciare l'eccezione passata; questa è la responsabilità del chiamante.

Speravo in una descrizione chiara degli argomenti del metodo __exit__. Questo è carente ma possiamo dedurli ...

Presumibilmente exc_type è la classe dell'eccezione.

Si dice che non si deve controrilanciare l'eccezione inoltrata. Questo ci suggerisce che uno degli argomenti potrebbe essere un'istanza di Eccezione reale ... o forse dovresti istanziarlo tu stesso dal tipo e dal valore?

Possiamo rispondere, cercando in questo articolo:
http://effbot.org/zone/python-with-statement.htm

Ad esempio, il metodo __exit__ seguente ingoia qualsiasi TypeError, ma lascia tutte le altre eccezioni attraverso:

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

. ..so chiaramente value è un'istanza Exception.

E presumibilmente traceback è un oggetto Python traceback.

6

Oltre alle risposte di cui sopra per esemplificare ordine invocazione, un semplice esempio corsa

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") 

produce l'output:

__init__ 
__enter__ 
body 
__exit__ 
__del__ 
+1

E anche se la sequenza delle definizioni viene cambiata, l'ordine di esecuzione rimane lo stesso! – Sean

Problemi correlati