Recherche…


Introduction

Bien que les gestionnaires de contexte de Python soient largement utilisés, peu de personnes comprennent le but de leur utilisation. Ces instructions, couramment utilisées avec les fichiers de lecture et d'écriture, aident l'application à conserver la mémoire système et à améliorer la gestion des ressources en garantissant que certaines ressources ne sont utilisées que pour certains processus. Cette rubrique explique et illustre l'utilisation des gestionnaires de contexte de Python.

Syntaxe

  • avec "context_manager" (comme "alias") (, "context_manager" (comme "alias")?) *:

Remarques

Les gestionnaires de contexte sont définis dans PEP 343 . Ils sont destinés à être utilisés comme mécanisme plus succinct pour la gestion des ressources que d' try ... finally construire. La définition formelle est la suivante.

Dans ce PEP, les gestionnaires de contexte fournissent les __enter__() et __exit__() qui sont appelées à l'entrée et à la sortie du corps de l'instruction with.

Il continue ensuite à définir l'instruction with comme suit.

with EXPR as VAR:
    BLOCK

La traduction de la déclaration ci-dessus est la suivante:

   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)

Introduction aux gestionnaires de contexte et à l'énoncé with

Un gestionnaire de contexte est un objet qui est notifié lorsqu'un contexte (un bloc de code) commence et se termine . Vous en utilisez généralement un avec l'instruction with . Il prend soin de la notification.

Par exemple, les objets de fichier sont des gestionnaires de contexte. Lorsqu'un contexte se termine, l'objet fichier est automatiquement fermé:

open_file = open(filename)
with open_file:
    file_contents = open_file.read()

# the open_file object has automatically been closed.

L'exemple ci-dessus est généralement simplifié en utilisant le mot-clé as :

with open(filename) as open_file:
    file_contents = open_file.read()

# the open_file object has automatically been closed.

Tout ce qui met fin à l'exécution du bloc provoque l'appel de la méthode de sortie du gestionnaire de contexte. Cela inclut des exceptions et peut être utile lorsqu'une erreur vous oblige à quitter prématurément un fichier ouvert ou une connexion. Quitter un script sans fermer correctement les fichiers / connexions est une mauvaise idée, susceptible de provoquer une perte de données ou d’autres problèmes. En utilisant un gestionnaire de contexte, vous pouvez vous assurer que des précautions sont toujours prises pour éviter les dommages ou les pertes de cette manière. Cette fonctionnalité a été ajoutée dans Python 2.5.

Affectation à une cible

De nombreux gestionnaires de contexte renvoient un objet lorsqu'ils sont entrés. Vous pouvez affecter cet objet à un nouveau nom dans l'instruction with .

Par exemple, l'utilisation d'une connexion de base de données dans une instruction with pourrait vous donner un objet curseur:

with database_connection as cursor:
    cursor.execute(sql_query)

Les objets fichiers retournent eux-mêmes, cela permet à la fois d'ouvrir l'objet fichier et de l'utiliser comme gestionnaire de contexte dans une expression:

with open(filename) as open_file:
    file_contents = open_file.read()

Ecrire votre propre gestionnaire de contexte

Un gestionnaire de contexte est un objet qui implémente deux méthodes magiques __enter__() et __exit__() (bien qu’il puisse également implémenter d’autres méthodes):

class AContextManager():

    def __enter__(self):
        print("Entered")
        # optionally return an object
        return "A-instance"

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exited" + (" (with an exception)" if exc_type else ""))
        # return True if you want to suppress the exception

Si le contexte sort avec une exception, les informations sur cette exception sera adoptée en tant que triple exc_type , exc_value , traceback (ce sont les mêmes variables que retournée par la sys.exc_info() fonction). Si le contexte se ferme normalement, ces trois arguments seront None .

Si une exception se produit et est transmise à la méthode __exit__ , la méthode peut renvoyer True afin de supprimer l'exception ou l'exception sera relancée à la fin de la fonction __exit__ .

with AContextManager() as a:
    print("a is %r" % a)
# Entered
# a is 'A-instance'
# Exited

with AContextManager() as a:
    print("a is %d" % a)
# Entered
# Exited (with an exception)
# Traceback (most recent call last):
#   File "<stdin>", line 2, in <module>
# TypeError: %d format: a number is required, not str

Notez que dans le deuxième exemple, même si une exception se produit au milieu du corps de l'instruction with-statement, le gestionnaire __exit__ toujours exécuté avant que l'exception ne se propage à la portée externe.

Si vous n'avez besoin que d'une méthode __exit__ , vous pouvez renvoyer l'instance du gestionnaire de contexte:

class MyContextManager:
    def __enter__(self):
        return self

    def __exit__(self):
        print('something')

Ecrire votre propre gestionnaire de contexte en utilisant la syntaxe du générateur

Il est également possible d'écrire un gestionnaire de contexte en utilisant la syntaxe du générateur grâce au décorateur contextlib.contextmanager :

import contextlib

@contextlib.contextmanager
def context_manager(num):
    print('Enter')
    yield num + 1
    print('Exit')

with context_manager(2) as cm:
    # the following instructions are run when the 'yield' point of the context
    # manager is reached.
    # 'cm' will have the value that was yielded
    print('Right in the middle with cm = {}'.format(cm))

produit:

Enter
Right in the middle with cm = 3
Exit

Le décorateur simplifie l'écriture d'un gestionnaire de contexte en convertissant un générateur en un seul. Avant que l'expression de rendement ne devienne la méthode __enter__ , la valeur __enter__ devient la valeur renvoyée par le générateur (qui peut être liée à une variable dans l'instruction with), et tout ce qui suit l'expression de rendement devient la méthode __exit__ .

Si une exception doit être gérée par le gestionnaire de contexte, un try..except..finally -block peut être écrit dans le générateur et toute exception déclenchée dans le bloc with -block sera traitée par ce bloc d'exception.

@contextlib.contextmanager
def error_handling_context_manager(num):
    print("Enter")
    try:
        yield num + 1
    except ZeroDivisionError:
        print("Caught error")
    finally:
        print("Cleaning up")
    print("Exit")

with error_handling_context_manager(-1) as cm:
    print("Dividing by cm = {}".format(cm))
    print(2 / cm)

Cela produit:

Enter
Dividing by cm = 0
Caught error
Cleaning up
Exit

Plusieurs gestionnaires de contexte

Vous pouvez ouvrir plusieurs gestionnaires de contenu en même temps:

with open(input_path) as input_file, open(output_path, 'w') as output_file:

    # do something with both files. 
    
    # e.g. copy the contents of input_file into output_file
    for line in input_file:
        output_file.write(line + '\n')

Il a le même effet que les gestionnaires de contexte d'imbrication:

with open(input_path) as input_file:
    with open(output_path, 'w') as output_file:
        for line in input_file:
            output_file.write(line + '\n')

Gérer les ressources

class File():
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        self.open_file = open(self.filename, self.mode)
        return self.open_file

    def __exit__(self, *args):
        self.open_file.close()

__init__() configure l'objet, en définissant le nom du fichier et le mode pour ouvrir le fichier. __enter__() s'ouvre et renvoie le fichier et __exit__() ferme.

L'utilisation de ces méthodes magiques ( __enter__ , __exit__ ) vous permet d'implémenter des objets facilement utilisables with l'instruction with.

Utiliser la classe de fichier:

for _ in range(10000):
    with File('foo.txt', 'w') as f:
        f.write('foo')


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow