J'ai vu cela dans le code de quelqu'un. Qu'est-ce que ça veut dire?
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
L'utilisation de ces méthodes magiques (__enter__
, __exit__
) vous permet d'implémenter des objets qui peuvent être facilement utilisés avec l'instruction with
.
L'idée est que cela facilite la construction de code nécessitant l'exécution d'un code de "nettoyage" (considérez-le comme un bloc try-finally
.). Quelques explications supplémentaires ici .
Un exemple utile pourrait être un objet de connexion à une base de données (qui ferme ensuite automatiquement la connexion lorsque l'instruction 'with' correspondante sort de la portée):
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 utiliser from __future__ import with_statement
en haut du fichier si vous êtes sur Python 2.5).
with DatabaseConnection() as mydbconn:
# do stuff
PEP343 - Le 'with' statement ' a aussi une belle écriture.
Si vous savez ce que gestionnaires de contexte vous n'avez alors plus besoin de comprendre les méthodes magiques __enter__
et __exit__
. Voyons un exemple très simple.
Dans cet exemple, j'ouvre myfile.txt avec l'aide de la fonction open. Le bloc try/finally garantit 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, j'ouvre le même fichier avec l'instruction with:
with open(r"C:\Users\SharpEl\Desktop\myfile.txt") as fp:
for line in fp:
print(line)
Si vous regardez le code, je n'ai pas fermé le fichier et il n'y a pas de bloc try/finally. Parce que avec, l'instruction se ferme automatiquement myfile.txt. Vous pouvez même le vérifier en appelant l'attribut print(fp.closed)
, qui renvoie True
.
En effet, les objets fichier (fp dans mon exemple) renvoyés par la fonction open ont deux méthodes intégrées __enter__
et __exit__
. Il est également appelé gestionnaire de contexte. La méthode __enter__
est appelée au début du bloc avec et la méthode __exit__
est appelée à la fin. Remarque: L'instruction with ne fonctionne qu'avec les objets prenant en charge le protocole de configuration de contexte, c'est-à-dire qu'ils utilisent les méthodes __enter__
et __exit__
. Une classe qui implémente les deux méthodes est appelée classe de gestionnaire de contexte.
Définissons maintenant notre propre classe gestionnaire de contexte.
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 vous avez maintenant une compréhension de base des deux méthodes magiques __enter__
et __exit__
.
J'ai trouvé étrangement difficile de localiser les méthodes python pour les méthodes __enter__
et __exit__
par Google, alors pour aider les autres, voici le lien:
https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers
(le détail est le même pour les deux versions)
object.__enter__(self)
Entrez le contexte d’exécution associé à cet objet. L’instructionwith
lie la valeur de retour de cette méthode à la ou aux cibles spécifiées dans la clause as de l’instruction, le cas échéant.
object.__exit__(self, exc_type, exc_value, traceback)
Quitte le contexte d’exécution associé à cet objet. Les paramètres décrivent l'exception à l'origine de la fermeture du contexte. Si le contexte a été quitté sans exception, les trois arguments serontNone
.Si une exception est fournie et que la méthode souhaite la supprimer (c'est-à-dire l'empêcher de se propager), elle devrait renvoyer une valeur vraie. Sinon, l'exception sera traitée normalement à la sortie de cette méthode.
Notez que les méthodes
__exit__()
ne doivent pas relancer l'exception transmise; C’est la responsabilité de l’appelant.
J'espérais une description claire des arguments de la méthode __exit__
. Cela manque mais on peut en déduire ...
Vraisemblablement exc_type
est la classe de l'exception.
Il dit que vous ne devriez pas relancer l'exception contournée. Cela nous suggère qu'un des arguments pourrait être une instance d'Exception réelle ... ou peut-être êtes-vous censé l'instancier vous-même à partir du type et de la valeur?
Nous pouvons répondre en regardant cet article:
http://effbot.org/zone/python-with-statement.htm
Par exemple, la méthode
__exit__
suivante englobe toutes les erreurs TypeError, mais laisse passer toutes les autres exceptions:
def __exit__(self, type, value, traceback):
return isinstance(value, TypeError)
... si clairement value
est une instance d'Exception.
Et vraisemblablement traceback
est un objet Python traceback .
Outre les réponses ci-dessus pour illustrer l'ordre d'appel, un exemple d'exécution simple
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__
Rappel: en utilisant la syntaxe with myclass() as mc
, la variable mc récupère la valeur renvoyée par __enter__()
, dans le cas ci-dessus None
! Pour une telle utilisation, il est nécessaire de définir une valeur de retour, telle que:
def __enter__(self):
print('__enter__')
return self
essayez d'ajouter mes réponses (ma pensée d'apprendre):
__enter__
et [__exit__]
sont des méthodes invoquées à l'entrée et à la sortie du corps de " l'instruction with " ( PEP 34 ) et la mise en oeuvre des deux est appelée gestionnaire de contexte.
l'instruction with a pour but de masquer le contrôle de flux de la clause try finally et de rendre le code impénétrable.
la syntaxe de l'instruction with est la suivante:
with EXPR as VAR:
BLOCK
qui se traduisent 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 du 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, essayez manuellement (en suivant la syntaxe de traduction):
>>> 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, merci ....