Spiegando di Python '__enter__' e '__exit__'
-
22-09-2019 - |
Domanda
ho visto questo nel codice di qualcuno. Che cosa significa?
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
Soluzione
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 rende facile costruire codice che ha bisogno di qualche codice 'cleandown' eseguito (pensare ad esso come un blocco try-finally
). Alcuni ulteriori spiegazioni qui .
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 questo oggetto con la dichiarazione with
(potrebbe essere necessario fare from __future__ import with_statement
nella parte superiore del file, se siete su Python 2.5).
with DatabaseConnection() as mydbconn:
# do stuff
PEP343 - La 'con' dichiarazione ' ha un interessante resoconto pure.
Altri suggerimenti
Se si conosce ciò che i manager di contesto sono allora avete bisogno di niente di più per capire __enter__
e __exit__
metodi magici. Vediamo un esempio molto semplice.
In questo esempio sto aprendo miofile.txt con l'aiuto di funzione open. Il Prova / finally blocco assicura che, anche se si verifica un'eccezione imprevista miofile.txt sarà 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 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 chiudere il file e non c'è try / finally di blocco. Perché dichiarazione si chiude automaticamente miofile.txt . È anche possibile controllare chiamando attributo print(fp.closed)
-. Che restituisce True
Questo è perché gli oggetti file (FP nel mio esempio) restituiti dal funzione open ha due built-in metodi __enter__
e __exit__
. E 'noto anche come contesto manager. Metodo __enter__
viene chiamato all'inizio del metodo di blocco e __exit__
viene chiamato alla fine. Nota: istruzione funziona solo con oggetti che supportano il protocollo contesto mamangement cioè hanno __enter__
e __exit__
metodi. Una classe che implementa entrambi i metodi è noto come classe contesto gestore.
Ora permette di definire la nostra contesto Manager di classe.
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 che ora avete conoscenza di base di entrambi i __enter__
e __exit__
metodi magici.
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 / riferimento / datamodel.html # con-dichiarazione-context-manager
https://docs.python.org/3/reference /datamodel.html#with-statement-context-managers
(Particolare è la stessa per entrambe le versioni)
object.__enter__(self)
Inserire il contesto di esecuzione relative a questo oggetto. La dichiarazionewith
legherà valore di ritorno di questo metodo per il target (s) specificato nel come clausola della dichiarazione, se del caso.
object.__exit__(self, exc_type, exc_value, traceback)
Uscire dal contesto di esecuzione relative a questo oggetto. I parametri descrivono l'eccezione che ha causato il contesto può uscire. Se il contesto è stato abbandonato, senza eccezione, sarannoNone
tutti i tre argomenti.Se un'eccezione viene alimentato, e il metodo desidera sopprimere l'eccezione (ossia impedirne propagato), deve restituire un valore vero. Altrimenti, l'eccezione viene elaborato normalmente all'uscita da questo metodo.
Si noti che i metodi di
__exit__()
non dovrebbe rilanciare l'eccezione passata-in; questo è responsabilità del chiamante.
speravo in una chiara descrizione degli argomenti del metodo __exit__
. Questo è carente, ma li possiamo dedurre ...
Presumibilmente exc_type
è la classe di eccezione.
Si dice che non dovrebbe rilanciare l'eccezione passata-in. Questo ci suggerisce che uno degli argomenti potrebbe essere un caso di eccezione effettivo ... o forse si suppone di creare un'istanza di voi stessi dal tipo e il valore?
Possiamo rispondere, cercando in questo articolo:
http://effbot.org/zone/python-with-statement.htm
Ad esempio, il seguente metodo
__exit__
ingoia qualsiasi TypeError, ma lascia tutte le altre eccezioni attraverso:
def __exit__(self, type, value, traceback):
return isinstance(value, TypeError)
... così chiaramente value
è un'istanza di eccezione.
E presumibilmente traceback
è un traceback Python obiettare.
Oltre alle risposte di cui sopra per esemplificare ordine invocazione, un semplice esempio run
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'uscita:
__init__
__enter__
body
__exit__
__del__
Un promemoria: quando si utilizza la sintassi di with myclass() as mc
, mc variabile ottiene il valore restituito da __enter__()
, nel caso di cui sopra None
! Per tale utilizzo, è necessario definire il valore di ritorno, come ad esempio:
def __enter__(self):
print('__enter__')
return self
prova ad aggiungere le mie risposte (il mio pensiero di apprendimento):
__enter__
e [__exit__]
entrambi sono metodi che vengono richiamati in entrata e l'uscita dal corpo di " l'istruzione with " ( PEP 343 ) e l'attuazione di entrambi è chiamato contesto manager.
l'istruzione with è intenzione di nascondere il controllo del flusso di provare finalmente clausola e rendere il codice imperscrutabile.
la sintassi del con affermazione è:
with EXPR as VAR:
BLOCK
, che si traducono in (come menzione nel PEP 343):
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)
provare qualche codice:
>>> 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'
ed ora cercare manualmente (segue tradurre sintassi):
>>> 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
il risultato del lato server stesso di prima
mi dispiace per il mio cattivo inglese e le mie spiegazioni poco chiare, grazie ....