122 votes

Implémentation de l'utilisation de "with object() as f" dans une classe personnalisée en python

Je dois ouvrir un objet de type fichier en python (il s'agit d'une connexion série via /dev/) et le fermer ensuite. Ceci est fait plusieurs fois dans plusieurs méthodes de ma classe. La façon dont je le faisais était d'ouvrir le fichier dans le constructeur, puis de le fermer dans le destructeur. Je reçois des erreurs bizarres et je pense que c'est lié au garbage collector et autres, je ne suis pas encore habitué à ne pas savoir exactement quand mes objets sont supprimés =\N

La raison pour laquelle j'ai fait cela est que je dois utiliser tcsetattr avec un tas de paramètres à chaque fois que je l'ouvre et cela devient ennuyeux de faire tout cela partout. Je veux donc implémenter une classe interne pour gérer tout cela afin que je puisse l'utiliser en faisant
with Meter('/dev/ttyS2') as m:

J'ai cherché en ligne et je n'ai pas trouvé de réponse vraiment satisfaisante sur la façon dont la with est mise en œuvre. J'ai vu qu'elle utilisait l'élément __enter__(self) y __exit(self)__ des méthodes. Mais est-ce qu'il me suffit d'implémenter ces méthodes pour pouvoir utiliser la syntaxe "with" ? Ou est-ce qu'il y a plus que cela ?

Existe-t-il un exemple sur la façon de procéder ou une documentation sur la façon dont cela est mis en œuvre sur les objets fichiers que je puisse consulter ?

165voto

dekomote Points 1578

Ces méthodes sont à peu près tout ce dont vous avez besoin pour faire fonctionner l'objet avec with déclaration.

En __enter__ vous devez renvoyer l'objet fichier après l'avoir ouvert et configuré.

En __exit__ vous devez fermer l'objet fichier. Le code permettant d'écrire dans le fichier se trouve dans l'objet with corps de l'énoncé.

class Meter():
    def __init__(self, dev):
        self.dev = dev
    def __enter__(self):
        #ttysetattr etc goes here before opening and returning the file object
        self.fd = open(self.dev, MODE)
        return self
    def __exit__(self, type, value, traceback):
        #Exception handling here
        close(self.fd)

meter = Meter('dev/tty0')
with meter as m:
    #here you work with the file object.
    m.fd.read()

66voto

Alex Martelli Points 330805

Le plus simple est peut-être d'utiliser le module standard de la bibliothèque Python contextlib :

import contextlib

@contextlib.contextmanager
def themeter(name):
    theobj = Meter(name)
    try:
        yield theobj
    finally:
        theobj.close()  # or whatever you need to do at exit

# usage
with themeter('/dev/ttyS2') as m:
    # do what you need with m
    m.read()

Cela ne rend pas Meter elle-même un gestionnaire de contexte (et n'est donc pas invasive pour cette classe), mais la "décore" (pas au sens de la "syntaxe de décorateur" de Python, mais plutôt presque, mais pas tout à fait, au sens du patron de conception de décorateur;-) avec une fonction d'usine themeter qui es un gestionnaire de contexte (que le contextlib.contextmanager se construit à partir du décorateur "single- yield La fonction "générateur" que vous écrivez) -- ce qui la rend donc permet de séparer plus facilement les conditions d'entrée et de sortie, évite l'imbrication, etc.

-13voto

Glenn Maynard Points 24451

Le premier résultat de Google (pour moi) l'explique assez simplement :

http://effbot.org/zone/python-with-statement.htm

et le PEP l'explique plus précisément (mais aussi plus verbeusement) :

http://www.python.org/dev/peps/pep-0343/

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