Expliquer « la __enter__ » et « __exit__ » de Python
-
22-09-2019 - |
Question
J'ai vu cela dans le code de quelqu'un. Qu'est-ce que cela signifie?
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
La solution
L'utilisation de ces méthodes magiques (__enter__
, __exit__
) vous permet d'implémenter des objets qui peuvent être utilisés facilement avec la déclaration de with
.
L'idée est qu'il rend facile de construire le code qui a besoin d'un code « cleandown » exécuté (pensez comme un bloc de try-finally
). plus d'explications ici .
Un exemple utile peut être un objet de connexion de base de données (qui se ferme alors automagically la connexion une fois que le « with'-instruction est hors de portée correspondante):
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()
...
Comme expliqué ci-dessus, utilisez cet objet avec l'instruction with
(vous devrez peut-être faire from __future__ import with_statement
en haut du fichier si vous êtes sur Python 2.5).
with DatabaseConnection() as mydbconn:
# do stuff
PEP343 - Le 'avec' déclaration ' a une belle writeup ainsi.
Autres conseils
Si vous savez ce que gestionnaires de contexte sont alors vous avez besoin plus rien à comprendre __enter__
et __exit__
méthodes magiques. Voyons un exemple très simple.
Dans cet exemple, je suis l'ouverture myfile.txt avec l'aide de open fonction . try / finally bloc assure que même si une exception inattendue se produit myfile.txt sera fermé.
fp=open(r"C:\Users\SharpEl\Desktop\myfile.txt")
try:
for line in fp:
print(line)
finally:
fp.close()
Maintenant, je suis l'ouverture même fichier avec déclaration :
with open(r"C:\Users\SharpEl\Desktop\myfile.txt") as fp:
for line in fp:
print(line)
Si vous regardez le code, je ne l'ai pas fermer le dossier et il n'y a pas try / finally bloc. Parce que instruction ferme automatiquement myfile.txt . Vous pouvez même vérifier en appelant attribut print(fp.closed)
-. Qui retourne True
Ceci est parce que les objets de fichiers (fp dans mon exemple) retournés par Ouvrir fonction a deux méthodes intégrées __enter__
et __exit__
. Il est également connu comme gestionnaire de contexte. Procédé de __enter__
est appelée au début de avec bloc et un procédé __exit__
est appelé à la fin. Remarque: déclaration ne fonctionne qu'avec des objets qui prennent en charge le protocole mamangement de contexte à savoir qu'ils ont des méthodes de __enter__
et __exit__
. Une classe qui mettent en œuvre les deux méthodes est connu comme classe gestionnaire de contexte.
permet maintenant de définir notre propre gestionnaire de contexte 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")
J'espère que maintenant vous avez des connaissances de base des deux méthodes magiques __enter__
et __exit__
.
Je l'ai trouvé étrangement difficile de localiser les docs python pour les méthodes de __enter__
et __exit__
par googler, afin d'aider les autres voici le lien:
https://docs.python.org/ 2 / référence / datamodel.html # avec-statement-contexte-gestionnaires
https://docs.python.org/3/reference /datamodel.html#with-statement-context-managers
(Détail est identique pour les deux versions)
object.__enter__(self)
Entrez le contexte d'exécution lié à cet objet. L'instructionwith
va se lier cette valeur de retour de la méthode à la cible (s) spécifié dans le titre de clause de la déclaration, le cas échéant.
object.__exit__(self, exc_type, exc_value, traceback)
Quittez le contexte d'exécution lié à cet objet. Les paramètres décrivent l'exception qui a causé le contexte à quitté. Si le contexte a été sorti sans exception, serontNone
trois arguments.Si une exception est fournie, et le procédé souhaite supprimer l'exception (à savoir, l'empêcher de se propager), il doit retourner une valeur vraie. Dans le cas contraire, l'exception sera traitée normalement à la sortie de cette méthode.
Notez que les méthodes de
__exit__()
ne doivent pas reraise l'exception adoptée en; c'est la responsabilité de l'appelant.
J'espérais une description claire des arguments de la méthode de __exit__
. Ce manque, mais nous pouvons les déduire ...
On peut supposer que exc_type
est la classe de l'exception.
Il dit que vous ne devrait pas re-soulever l'exception passée en. Cela nous amène à penser que l'un des arguments est peut-être une instance réelle d'exception ... ou peut-être vous censé ce instancier vous du type et la valeur?
Nous pouvons répondre en regardant cet article:
http://effbot.org/zone/python-with-statement.htm
Par exemple, la méthode
__exit__
suivant avalait l'TypeError, mais laisse toutes les autres exceptions par:
def __exit__(self, type, value, traceback):
return isinstance(value, TypeError)
... si clairement value
est une instance d'exception.
Et sans doute traceback
est un Python retraçage objet.
En plus des réponses ci-dessus pour illustrer l'ordre d'invocation, un exemple simple d'exécution
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")
Produit la sortie:
__init__
__enter__
body
__exit__
__del__
Un rappel: lorsque vous utilisez la syntaxe with myclass() as mc
, mc variable devient la valeur retournée par __enter__()
, dans le cas None
ci-dessus! Pour une telle utilisation, doivent définir la valeur de retour, tels que:
def __enter__(self):
print('__enter__')
return self
essayer d'ajouter mes réponses (ma pensée de l'apprentissage):
__enter__
et [__exit__]
les deux sont des méthodes qui sont invoquées à l'entrée et à la sortie du corps de « avec la déclaration » ( PEP 343 ) et la mise en œuvre des deux est appelé gestionnaire de contexte.
avec la déclaration est l'intention de cacher le contrôle de flux d'essayer finally et rendre le code indéchiffrable.
la syntaxe de l'instruction with est:
with EXPR as VAR:
BLOCK
qui se traduire par (comme mentionné dans 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)
essayez un code:
>>> 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'
et maintenant essayer manuellement (suivant traduire la syntaxe):
>>> 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
le résultat du côté serveur comme avant
désolé pour mon mauvais anglais et mes explications peu claires, je vous remercie ....