29 votes

Comprendre le Python avec les gestionnaires de déclarations et de contexte

J'essaie de comprendre le with déclaration. Je comprends qu'elle est censée remplacer la try / except bloc.

Maintenant, supposons que je fasse quelque chose comme ça :

try:
   name = "rubicon" / 2  # to raise an exception
except Exception as e:
   print("No, not possible.")
finally:
   print("OK, I caught you.")

Comment puis-je remplacer cela par un gestionnaire de contexte ?

32voto

Paul Price Points 655

El contextlib.contextmanager offre un moyen pratique de fournir un gestionnaire de contexte sans avoir besoin d'écrire une véritable fonction ContextManager de votre propre classe (avec __enter__ y __exit__ afin que vous n'ayez pas à vous souvenir des arguments de la méthode __exit__ ou que la méthode __exit__ La méthode doit return True afin de supprimer l'exception). Au lieu de cela, vous écrivez une fonction avec un seul yield à l'endroit où vous voulez que le with pour qu'il s'exécute, et vous piégez toute exception (qui provient effectivement de l'application yield ) comme vous le feriez normalement.

from contextlib import contextmanager
@contextmanager
def handler():
    # Put here what would ordinarily go in the `__enter__` method
    # In this case, there's nothing to do
    try:
        yield # You can return something if you want, that gets picked up in the 'as'
    except Exception as e:
        print "no not possible"
    finally:
        print "Ok I caught you"

with handler():
    name='rubicon'/2 #to raise an exception

Pourquoi se donner la peine d'écrire un gestionnaire de contexte ? Réutilisation du code. Vous pouvez utiliser le même gestionnaire de contexte à plusieurs endroits, sans avoir à dupliquer le traitement des exceptions. Si le traitement des exceptions est unique à cette situation, alors ne vous embêtez pas avec un gestionnaire de contexte. Mais si le même schéma se répète sans cesse (ou s'il peut l'être pour vos utilisateurs, par exemple la fermeture d'un fichier, le déverrouillage d'un mutex), cela vaut la peine de se donner du mal. C'est également un bon modèle à utiliser si la gestion des exceptions est un peu compliquée, car il sépare la gestion des exceptions du flux de code principal.

31voto

Alex Martelli Points 330805

with ne remplace pas vraiment try / except mais, plutôt, try / finally . Pourtant, vous peut faire en sorte qu'un gestionnaire de contexte fasse quelque chose de différent dans les cas d'exception et dans les cas sans exception :

class Mgr(object):
    def __enter__(self): pass
    def __exit__(self, ext, exv, trb):
        if ext is not None: print "no not possible"
        print "OK I caught you"
        return True

with Mgr():
    name='rubicon'/2 #to raise an exception

El return True C'est là que le gestionnaire de contexte décide de supprimer l'exception (comme vous le faites en ne la levant pas à nouveau dans votre programme de gestion des exceptions). except clause).

15voto

El with en Python est destiné à envelopper un ensemble d'instructions où vous devez mettre en place et détruire ou fermer des ressources. Il est en quelque sorte similaire à try...finally à cet égard, car la clause finally sera exécutée même après une exception.

Un gestionnaire de contexte est un objet qui implémente deux méthodes : __enter__ y __exit__ . Ceux-ci sont appelés immédiatement avant et après (respectivement) l'appel à la fonction with bloc.

Par exemple, regardez le classique open() exemple :

with open('temp.txt', 'w') as f:
    f.write("Hi!")

Open renvoie un File qui implémente __enter__ plus ou moins comme return self y __exit__ comme self.close() .

10voto

jack yang Points 652

Les composants du gestionnaire de contexte

  1. Vous devez mettre en œuvre un __enter__ qui renvoie un objet
  2. Mettre en œuvre un __exit__ méthode.

Exemple

Je vais vous donner un exemple simple pour vous montrer pourquoi nous avons besoin d'un gestionnaire de contexte. Pendant l'hiver à Xinjiang, en Chine, vous devez fermer une porte immédiatement après l'avoir ouverte. Si vous oubliez de la fermer, vous aurez froid.

 class Door:
     def __init__(self):
         self.doorstatus='the door was closed when you are not at home'
         print(self.doorstatus)
     def __enter__(self):
         print('I have opened the door')
         return self
     def __exit__(self,*args):
         print('pong!the door has closed')
     def fetchsomethings(self):
         print('I have fetched somethings')

pour aller chercher des choses à la maison, il faut ouvrir une porte, aller chercher quelque chose et fermer la porte.

 with Door() as dr:
     dr.fetchsomethings()

le résultat est :

the door was closed when you are not at home
I have opened the door
I have fetched somethings
pong!the door has closed

Explication

lorsque vous lancez une classe de porte, elle appelle __init__ qui imprimera "la porte a été fermée lorsque vous n'êtes pas à la maison" et __enter__ qui imprimera "J'ai ouvert la porte" et renverra une instance de porte appelée dr. Lorsque l'on appelle self.fetchsomethings dans le bloc, la méthode imprimera "J'ai récupéré quelque chose".lorsque le bloc est terminé, le gestionnaire de contexte invoquera __exit__ et il imprimera "pong!la porte s'est fermée" .si vous n'utilisez pas le mot clé with mot-clé , __enter__ y __exit__ ne sera pas invoquée !!!!

5voto

Bharel Points 5784

with Les déclarations ou les gestionnaires de contexte sont là pour aider les ressources (bien qu'ils puissent être utilisés pour bien d'autres choses).

Disons que vous avez ouvert un fichier en écriture :

f = open(path, "w")

Vous avez maintenant un handle de fichier ouvert. Pendant la manipulation de votre fichier, aucun autre programme ne peut y écrire. Pour permettre à d'autres programmes d'y écrire, vous devez fermer l'identificateur de fichier :

f.close()

Mais, avant de fermer votre fichier, une erreur s'est produite :

f = open(path, "w")
data = 3/0  # Tried dividing by zero. Raised ZeroDivisionError
f.write(data)
f.close()

Ce qui va se passer maintenant, c'est que la fonction ou le programme entier va se terminer, tout en laissant votre fichier avec un handle ouvert. (CPython nettoie les handles à la fin du programme et les handles sont libérés en même temps que le programme, mais il ne faut pas compter là-dessus).

Une instruction with garantit que dès que vous quittez l'indentation, elle fermera le gestionnaire de fichier :

with open(path, "w") as f:
    data = 3/0  # Tried dividing by zero. Raised ZeroDivisionError
    f.write(data)
# In here the file is already closed automatically, no matter what happened.

with Les déclarations peuvent être utilisées pour bien d'autres choses encore. Par exemple : threading.Lock()

lock = threading.Lock()
with lock:  # Lock is acquired
   do stuff...
# Lock is automatically released.

Presque tout ce qui est fait avec un gestionnaire de contexte peut être fait avec try: ... finally: ... mais les gestionnaires de contexte sont plus agréables à utiliser, plus confortables, plus lisibles et, grâce à l'implémentation des __enter__ y __exit__ fournir une interface facile à utiliser.


La création de gestionnaires de contexte se fait en mettant en œuvre __enter__() y __exit__() dans une classe normale.

__enter__() indique ce qu'il faut faire lorsqu'un gestionnaire de contexte démarre et __exit__() lorsqu'un gestionnaire de contexte existe (en donnant l'exception au __exit__() si une exception s'est produite)

Un raccourci pour créer des gestionnaires de contexte peut être trouvé dans contextlib . Il enveloppe un générateur comme gestionnaire de contexte.

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