web-dev-qa-db-fra.com

A quoi sert l'instruction python "with"?

Je suis tombé sur l'instruction Python with pour la première fois aujourd'hui. J'utilise Python à la légère depuis plusieurs mois et je ne connaissais même pas son existence! Vu son statut quelque peu obscur, j'ai pensé qu'il valait la peine de demander:

  1. A quoi sert l'instruction Python with?
  2. Qu'utilisez vous pour ça?
  3. Y a-t-il des pièges dont je devrais être au courant ou des anti-modèles communs associés à son utilisation? Dans les cas où il vaut mieux utiliser try..finally que with?
  4. Pourquoi n'est-il pas utilisé plus largement?
  5. Quelles classes de bibliothèque standard sont compatibles avec cela?
384
fmark
  1. Je crois que d'autres utilisateurs avant moi ont déjà répondu à cette question. Je ne l'ajoute donc que par souci d'exhaustivité: l'instruction with simplifie la gestion des exceptions en encapsulant les tâches de préparation et de nettoyage courantes dans ce qu'on appelle gestionnaires de contexte . Plus de détails peuvent être trouvés dans PEP 34 . Par exemple, l'instruction open est un gestionnaire de contexte en lui-même, qui vous permet d'ouvrir un fichier, de le garder ouvert tant que l'exécution est dans le contexte de l'instruction with où vous l'avez utilisé, et de le fermer dès que vous quittez le fichier. contexte, peu importe si vous l’avez quitté à cause d’une exception ou au cours d’un flux de contrôle régulier. L'instruction with peut donc être utilisée de manière similaire au motif RAII en C++: certaines ressources sont acquises par l'instruction with et libérées lorsque vous quittez le contexte with.

  2. En voici quelques exemples: ouverture de fichiers à l'aide de with open(filename) as fp:, acquisition de verrous à l'aide de with lock: (où lock est une instance de threading.Lock). Vous pouvez également créer vos propres gestionnaires de contexte à l'aide du décorateur contextmanager à partir de contextlib. Par exemple, je l'utilise souvent lorsque je dois modifier temporairement le répertoire en cours, puis revenir à l'endroit où j'étais:

    from contextlib import contextmanager
    import os
    
    @contextmanager
    def working_directory(path):
        current_dir = os.getcwd()
        os.chdir(path)
        try:
            yield
        finally:
            os.chdir(current_dir)
    
    with working_directory("data/stuff"):
        # do something within data/stuff
    # here I am back again in the original working directory
    

    Voici un autre exemple qui redirige temporairement sys.stdin, sys.stdout et sys.stderr vers un autre descripteur de fichier et les restaure ultérieurement:

    from contextlib import contextmanager
    import sys
    
    @contextmanager
    def redirected(**kwds):
        stream_names = ["stdin", "stdout", "stderr"]
        old_streams = {}
        try:
            for sname in stream_names:
                stream = kwds.get(sname, None)
                if stream is not None and stream != getattr(sys, sname):
                    old_streams[sname] = getattr(sys, sname)
                    setattr(sys, sname, stream)
            yield
        finally:
            for sname, stream in old_streams.iteritems():
                setattr(sys, sname, stream)
    
    with redirected(stdout=open("/tmp/log.txt", "w")):
         # these print statements will go to /tmp/log.txt
         print "Test entry 1"
         print "Test entry 2"
    # back to the normal stdout
    print "Back to normal stdout again"
    

    Et enfin, un autre exemple qui crée un dossier temporaire et le nettoie en quittant le contexte:

    from tempfile import mkdtemp
    from shutil import rmtree
    
    @contextmanager
    def temporary_dir(*args, **kwds):
        name = mkdtemp(*args, **kwds)
        try:
            yield name
        finally:
            shutil.rmtree(name)
    
    with temporary_dir() as dirname:
        # do whatever you want
    
369
Tamás

Je suggérerais deux conférences intéressantes:

  • PEP 34 La déclaration "avec"
  • Effbot Comprendre la déclaration "with" de Python

1. L'instruction with encapsule l'exécution d'un bloc avec des méthodes définies par un gestionnaire de contexte. Cela permet aux modèles d'utilisation try...except...finally courants d'être encapsulés pour une réutilisation pratique.

2. Vous pouvez faire quelque chose comme:

with open("foo.txt") as foo_file:
    data = foo_file.read()

OR

from contextlib import nested
with nested(A(), B(), C()) as (X, Y, Z):
   do_something()

OU (Python 3.1)

with open('data') as input_file, open('result', 'w') as output_file:
   for line in input_file:
     output_file.write(parse(line))

OR

lock = threading.Lock()
with lock:
    # Critical section of code

3. Je ne vois pas d’Antipattern ici.
Citer Plongez dans Python :

try..finally is good. avec c'est mieux.

4. J'imagine que c'est lié à l'habitude des programmeurs d'utiliser l'instruction try..catch..finally provenant d'autres langues.

85
systempuntoout

L'instruction Python with prend en charge le langage intégré de l'idiome Resource Acquisition Is Initialization couramment utilisé en C++. Il est conçu pour permettre l'acquisition et la libération sécurisées des ressources du système d'exploitation.

L'instruction with crée des ressources dans une étendue/un bloc. Vous écrivez votre code en utilisant les ressources du bloc. Lorsque le bloc se ferme, les ressources sont libérées proprement, quel que soit le résultat du code contenu dans le bloc (que le bloc se ferme normalement ou à cause d'une exception).

De nombreuses ressources de la bibliothèque Python qui obéissent au protocole requis par l'instruction with et peuvent donc être utilisées avec celle-ci immédiatement. Cependant, n'importe qui peut créer des ressources utilisables dans une instruction with en implémentant le protocole bien documenté: PEP 034

Utilisez-le chaque fois que vous acquérez dans votre application des ressources qui doivent être explicitement abandonnées, telles que des fichiers, des connexions réseau, des verrous, etc.

37
Tendayi Mawushe

Pour être encore plus complet, j'ajouterai mon cas d'utilisation le plus utile pour les instructions with.

Je fais beaucoup de calcul scientifique et pour certaines activités, j'ai besoin de la bibliothèque Decimal pour des calculs de précision arbitraires. Une partie de mon code nécessite une grande précision et, pour la plupart des autres, une précision moindre.

Je règle ma précision par défaut sur un nombre bas, puis utilise with pour obtenir une réponse plus précise pour certaines sections:

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

Je l’utilise beaucoup avec le test hypergéométrique qui nécessite la division de grands nombres résultant en factorielles de forme. Lorsque vous effectuez des calculs à l'échelle génomique, vous devez faire attention aux erreurs d'arrondi et de débordement.

25
JudoWill

Un exemple d'antipattern pourrait consister à utiliser with à l'intérieur d'une boucle lorsqu'il serait plus efficace de disposer de with à l'extérieur de la boucle.

par exemple

for row in lines:
    with open("outfile","a") as f:
        f.write(row)

contre

with open("outfile","a") as f:
    for row in lines:
        f.write(row)

La première méthode consiste à ouvrir et à fermer le fichier pour chaque row, ce qui peut entraîner des problèmes de performances par rapport à la deuxième méthode avec ouvre et ferme le fichier une seule fois.

25
John La Rooy

Voir PEP 343 - L'instruction 'with' , il y a un exemple de section à la fin.

... nouvelle instruction "with" dans le langage Python pour permettre d'extraire les utilisations standard des instructions try/finally.

9
stefanB

les points 1, 2 et 3 étant raisonnablement bien couverts:

4: il est relativement nouveau, disponible uniquement en python2.6 + (ou python2.5 en utilisant from __future__ import with_statement)

4
cobbal

L'instruction with fonctionne avec ce qu'on appelle des gestionnaires de contexte:

http://docs.python.org/release/2.5.2/lib/typecontextmanager.html

L'idée est de simplifier la gestion des exceptions en effectuant le nettoyage nécessaire après avoir quitté le bloc 'with'. Certains des éléments intégrés python fonctionnent déjà en tant que gestionnaires de contexte.

3
zefciu

Dans python, l'instruction généralement "with" est utilisée pour ouvrir un fichier, traiter les données présentes dans le fichier et également pour fermer le fichier sans appeler de méthode close (). L'instruction "with" simplifie la gestion des exceptions en fournissant des activités de nettoyage.

Forme générale de avec:

with open(“file name”, “mode”) as file-var:
    processing statements

note: pas besoin de fermer le fichier en appelant close () sur file-var.close ()

2
Tushar.PUCSD

Un autre exemple de prise en charge immédiate, et qui peut paraître un peu déroutant au début, quand vous êtes habitué à la façon dont la fonction intégrée open() se comporte, sont les objets connection des modules de base de données courants. tel que:

Les objets connection sont des gestionnaires de contexte et, en tant que tels, peuvent être utilisés directement dans un with-statement, toutefois, lorsque vous utilisez la remarque ci-dessus, notez que:

Lorsque le with-block est terminé, avec ou sans exception, , la connexion n'est pas fermée . Si with-block se termine avec une exception, la transaction est annulée, sinon la transaction est validée.

Cela signifie que le programmeur doit prendre soin de fermer la connexion eux-mêmes, mais permet d’acquérir une connexion et de l’utiliser dans plusieurs with-statements, comme indiqué dans le psycopg2 docs :

conn = psycopg2.connect(DSN)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL1)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL2)

conn.close()

Dans l'exemple ci-dessus, vous remarquerez que les objets cursor de psycopg2 sont également des gestionnaires de contexte. De la documentation pertinente sur le comportement:

Quand un cursor quitte le with-block il est fermé, libérant ainsi toute ressource éventuellement associée. L'état de la transaction n'est pas affecté.

2
bgse