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
Était-ce utile?

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'instruction with 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, seront None 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 ....

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top