web-dev-qa-db-fra.com

Comment "avec" est meilleur que try / catch pour ouvrir un fichier en Python?

J'ai compris que l'instruction with vous aide à transformer ceci:

try:
    f = open(my_file)
    do_stuff_that_fails()
except:
    pass
finally:
    f.close()

Dans:

with open(my_file) as f:
    do_stuff_that_fails()

Mais comment est-ce mieux? Vous devez toujours gérer le cas avec le fichier ne pouvant pas être ouvert (comme inviter l'utilisateur à lui dire qu'il n'a pas d'autorisations), donc en réalité vous auriez:

try:
    with open(my_file) as f:
        do_stuff_that_fails()
except (IOError, OSError, Failure) as e:
    do_stuff_when_it_doesnt_work()

Ce qui équivaut à:

try:
    f = open(my_file)
    do_stuff_that_fails()
except (IOError, OSError, Faillure) as e:
    do_stuff_when_it_doesnt_work()
finally:
    f.close()

Oui, vous avez gagné deux lignes, mais vous avez ajouté un niveau d'imbrication qui ne facilite pas la lecture. Le but de l'instruction with est-il de vous faire économiser deux lignes ou manque-t-il quelque chose?

Il semble beaucoup d'ajouter un mot-clé juste pour ça, donc j'ai l'impression qu'il y a une syntaxe pour gérer les essais supplémentaires/sauf que je ne sais pas.

40
e-satis

Pour commencer, cela permet d'éviter le problème que vous avez introduit dans votre exemple try ... finally ....

De la façon dont vous l'avez structuré, si une exception est levée lors de l'ouverture du fichier, vous ne lierez jamais un fichier ouvert au nom f, ce qui entraînera soit un NameError dans le finally clause (si f n'a jamais été lié dans la portée) ou quelque chose de totalement inattendu (si c'est le cas).

La structure correcte (équivalente à with) est:

f = open(my_file)

try:
    do_stuff_that_fails()
finally:
    f.close()

(note - pas besoin d'une clause except si vous n'avez rien à y faire).

De même, votre deuxième exemple est erroné et devrait être structuré comme suit:

try:
    f = open(my_file)

    try:
        do_stuff_that_fails()
    except EXPECTED_EXCEPTION_TYPES as e:
        do_stuff_when_it_doesnt_work()
    finally:
        f.close()

except (IOError, OSError) as e:
    do_other_stuff_when_it_we_have_file_IO_problems()

La seconde est (comme indiqué dans une autre réponse) que vous ne pouvez pas oublier d'appeler f.close().

BTW, le terme est "gestion de contexte", pas "gestion de ressources" - l'instruction with gère contextes, dont certaines peuvent être des ressources, d'autres non. Par exemple, il est également utilisé avec decimal pour établir un contexte décimal pour un bloc de code particulier.

Enfin (en réponse à votre commentaire à la réponse précédente), vous ne devriez jamais vous fier à la sémantique refcount pour gérer les ressources en Python. Jython, IronPython et PyPy ont tous une sémantique non-refcount, et rien n'empêche CPython d'aller dans l'autre sens (bien que cela soit hautement improbable dans un avenir immédiat). Dans une boucle étroite (par exemple os.walk), Il est très très facile de manquer de descripteurs de fichiers si le code reposant sur la sémantique refcount est exécuté sur un VM avec un comportement différent.

34
Tim Delaney

Dans l'exemple que vous donnez, ce n'est pas mieux. Il est recommandé d'attraper les exceptions au plus près du point où elles sont levées pour éviter d'attraper des exceptions non liées du même type.

try:
    file = open(...)
except OpenErrors...:
    # handle open exceptions
else:
    try:
        # do stuff with file
    finally:
        file.close()

Aussi verbeuse que cela puisse être, l'instruction with ne vous permet pas de détecter les exceptions levées lors de son évaluation. Il y avait un suggestion pour ajouter la gestion des exceptions à cet effet sur la liste de diffusion:

with open(...) as file:
    # do stuff with file
except OpenErrors...:
    # handle open exceptions

Mais c'était abatt .

Enfin, il convient de noter que vous pouvez directement entrer et quitter des gestionnaires de contexte comme ceci:

file = open(...).__enter__()
file.__exit__(typ, val, tb)

Ceci est décrit plus en détail ici et ici .

En règle générale, with indique Excel pour les cas où aucune exception n'est attendue et où le comportement par défaut "entrer/ouvrir/acquérir" est adéquat. Les exemples incluent les fichiers requis et le verrouillage simple.

16
Matt Joiner

C'est pour gestion des ressources ... pas pour la façon dont vous réagissez à une exception sinon :)

Il n'y a aucun moyen "d'oublier" f.close() lorsque vous utilisez with. De cette manière, il joue le même rôle que using en C #.

Codage heureux.

7
user166390