440 votes

Explication des fonctions "__enter__" et "__exit__" de Python

J'ai vu ça 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

20 votes

Une bonne explication ici : effbot.org/zone/python-with-statement.htm

7 votes

@StevenVascellaro Modifier le code d'une question est généralement une mauvaise idée, notamment lorsqu'il y a des erreurs dans le code. Cette question a été posée en pensant à Py2, et il n'y a aucune raison de la mettre à jour pour Py3.

514voto

ChristopheD Points 38217

En utilisant ces méthodes magiques ( __enter__ , __exit__ ) vous permet d'implémenter des objets qui peuvent être utilisés facilement avec la fonction with déclaration.

L'idée est de faciliter la construction d'un code qui nécessite l'exécution d'un code de "nettoyage" (pensez-y comme une try-finally bloc). Quelques explications supplémentaires ici .

Un exemple utile pourrait être un objet de connexion à une base de données (qui ferme automatiquement la connexion lorsque l'instruction "with" correspondante sort de sa 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 le with (vous devrez peut-être faire from __future__ import with_statement en haut du fichier si vous êtes sous Python 2.5).

with DatabaseConnection() as mydbconn:
    # do stuff

PEP343 -- La déclaration "avec". a également un bon article.

35 votes

Probablement, __enter__ devrait retourner self toujours car alors seules les autres méthodes de la classe peuvent être appelées sur le contexte.

6 votes

@ViFI Il y a 4 exemples de def __enter__(self) dans le PEP 343 et personne ne le fait return self : python.org/dev/peps/pep-0343 . Pourquoi pensez-vous cela ?

4 votes

@Grief : Pour 2 raisons, à mon avis, 1) je ne pourrai pas appeler d'autres méthodes sur self comme expliqué ici : stackoverflow.com/questions/38281853/ 2) self.XYZ n'est qu'une partie de l'objet self et renvoyer l'identifiant à cette seule partie me semble inapproprié du point de vue de la maintenance. Je préférerais renvoyer l'identifiant à l'objet complet et ensuite fournir des API publiques uniquement pour ces composants. self que je veux exposer à l'utilisateur comme dans with open(abc.txt, 'r') as fin: content = fin.read()

101voto

N Randhawa Points 2888

Si vous savez ce que gestionnaires de contexte sont alors vous n'avez besoin de rien de plus pour comprendre __enter__ y __exit__ des méthodes magiques. Voyons un exemple très simple.

Dans cet exemple, j'ouvre le monfichier.txt avec l'aide de ouvrir fonction. Le site essayer/finalement Le bloc assure que même si une exception inattendue se produit monfichier.txt seront fermés.

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 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 n'ai pas fermé le fichier et il n'y a pas de essayer/finalement bloc. Parce que avec la déclaration ferme automatiquement monfichier.txt . Vous pouvez même le vérifier en appelant print(fp.closed) qui renvoie l'attribut True .

Ceci est dû au fait que les objets fichiers (fp dans mon exemple) retournés par ouvrir a deux méthodes intégrées __enter__ y __exit__ . Il est également connu sous le nom de gestionnaire de contexte. __enter__ est appelée au début de avec et __exit__ est appelée à la fin.

Note : avec ne fonctionne qu'avec les objets qui prennent en charge le protocole de gestion de contexte (c'est-à-dire qu'ils ont __enter__ y __exit__ méthodes). Une classe qui met en œuvre les deux méthodes est connue comme classe de gestionnaire de contexte.

Maintenant, définissons 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 vous avez maintenant une compréhension de base des deux __enter__ y __exit__ des méthodes magiques.

80voto

Anentropic Points 7751

J'ai trouvé qu'il était étrangement difficile de localiser la documentation python relative à __enter__ y __exit__ en cherchant sur Google, donc 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
(les détails sont les mêmes pour les deux versions)

object.__enter__(self)
Entrez le contexte d'exécution lié à cet objet. Le site with liera 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 lié à cet objet. Les paramètres décrivent l'exception qui a provoqué la sortie du contexte. Si le contexte a été quitté sans exception, les trois arguments sont les suivants None .

Si une exception est fournie et que la méthode souhaite supprimer cette exception (c'est-à-dire empêcher sa propagation), elle doit renvoyer une valeur vraie. Sinon, l'exception sera traitée normalement à la sortie de cette méthode.

Notez que __exit__() ne doivent pas relancer l'exception transmise ; cela relève de la responsabilité de l'appelant.

J'espérais une description claire de la __exit__ les arguments de la méthode. Cela manque mais on peut les déduire...

Probablement exc_type est la classe de l'exception.

Il est dit que vous ne devez pas relancer l'exception passée. Cela nous suggère que l'un des arguments pourrait être une instance d'exception réelle... ou peut-être que vous êtes 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, les éléments suivants __exit__ avale toute 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 Python Traceback objet.

70voto

juncode Points 11

En plus des réponses ci-dessus pour illustrer l'ordre d'invocation, 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__

Un rappel : lorsque vous utilisez la syntaxe with myclass() as mc la variable mc reçoit la valeur retournée par __enter__() dans le cas ci-dessus None ! Pour une telle utilisation, il faut définir une valeur de retour, par exemple :

def __enter__(self): 
    print('__enter__')
    return self

6voto

Rohola Zandie Points 105

C'est ce qu'on appelle le gestionnaire de contexte et je veux juste ajouter que des approches similaires existent pour d'autres langages de programmation. Les comparer pourrait être utile pour comprendre le gestionnaire de contexte en python. Fondamentalement, un gestionnaire de contexte est utilisé lorsque nous avons affaire à des ressources (fichier, réseau, base de données) qui doivent être initialisées et, à un moment donné, détruites (disposées). Dans Java 7 et au-dessus nous avons la gestion automatique des ressources qui prend la forme de :

//Java code
try (Session session = new Session())
{
  // do stuff
}

Notez que Session doit implémenter AutoClosable ou l'une de ses (nombreuses) sous-interfaces.

Sur C# nous avons des déclarations d'utilisation pour la gestion des ressources qui prennent la forme de :

//C# code
using(Session session = new Session())
{
  ... do stuff.
}

Dans laquelle Session devrait mettre en œuvre IDisposable .

Sur python la classe que nous utilisons doit implémenter __enter__ y __exit__ . Elle prend donc la forme de :

#Python code
with Session() as session:
    #do stuff

Et comme d'autres l'ont souligné, vous pouvez toujours utiliser l'instruction try/finally dans tous les langages pour mettre en œuvre le même mécanisme. Ce n'est que du sucre syntaxique.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X