Utilisation de l'instruction Python «with» avec le bloc try-except

96

Est-ce la bonne façon d'utiliser l'instruction python "with" en combinaison avec un bloc try-except?:

try:
    with open("file", "r") as f:
        line = f.readline()
except IOError:
    <whatever>

Si tel est le cas, compte tenu de l'ancienne façon de faire les choses:

try:
    f = open("file", "r")
    line = f.readline()
except IOError:
    <whatever>
finally:
    f.close()

Est-ce que le principal avantage de la déclaration «avec» ici est que nous pouvons nous débarrasser de trois lignes de code? Cela ne me semble pas convaincant pour ce cas d'utilisation (même si je comprends que l'instruction "with" a d'autres utilisations).

EDIT: La fonctionnalité des deux blocs de code ci-dessus est-elle identique?

EDIT2: Les premières réponses parlent généralement des avantages d'utiliser "avec", mais ceux-ci semblent être marginaux ici. Nous avons tous (ou aurions dû) appeler explicitement f.close () depuis des années. Je suppose qu'un avantage est que les codeurs bâclés bénéficieront de l'utilisation de "avec".

gaefan
la source
Pour moi, ne pas avoir à me souvenir de fermer () des éléments dans une instruction finally est une raison suffisante pour utiliser «avec». J'ai vu beaucoup de code ne pas fermer ses ressources. Et «avec» n'a pas d'inconvénients pour autant que je puisse le voir.
Raúl Salinas-Monteagudo

Réponses:

139
  1. Les deux blocs de code que vous avez donnés ne sont pas équivalents
  2. Le code que vous avez décrit comme une ancienne façon de faire a un bogue sérieux: au cas où l'ouverture du fichier échoue, vous obtiendrez une deuxième exception dans la finallyclause car elle fn'est pas liée.

L'ancien code équivalent serait:

try:
    f = open("file", "r")
    try:
        line = f.readline()
    finally:
        f.close()
except IOError:
    <whatever>

Comme vous pouvez le voir, l' withinstruction peut rendre les choses moins sujettes aux erreurs. Dans les versions plus récentes de Python (2.7, 3.1), vous pouvez également combiner plusieurs expressions dans une seule withinstruction. Par exemple:

with open("input", "r") as inp, open("output", "w") as out:
    out.write(inp.read())

En plus de cela, je considère personnellement que c'est une mauvaise habitude d'attraper une exception le plus tôt possible. Ce n'est pas le but des exceptions. Si la fonction IO qui peut échouer fait partie d'une opération plus compliquée, dans la plupart des cas, IOError doit abandonner toute l'opération et donc être gérée à un niveau externe. En utilisant des withdéclarations, vous pouvez vous débarrasser de toutes ces try...finallydéclarations aux niveaux internes.

Bernd Petersohn
la source
7

Si le contenu du finallybloc est déterminé par les propriétés de l'objet fichier en cours d'ouverture, pourquoi l'implémenteur de l'objet fichier ne devrait-il pas être celui qui écrit le finallybloc? C'est l'avantage de l' withinstruction, bien plus que de vous faire économiser trois lignes de code dans cette instance particulière.

Et oui, la façon dont vous avez combiné withet try-exceptest à peu près la seule façon de le faire, car les erreurs exceptionnelles causées dans l' openinstruction elle-même ne peuvent pas être détectées dans le withbloc.

Peter Milley
la source
1

Je pense que vous vous êtes trompé sur l'affirmation «avec» selon laquelle il ne fait que réduire les lignes. Il effectue en fait l'initialisation et gère le démontage.

Dans votre cas, "avec" fait

  • ouvrir un fichier,
  • traiter son contenu, et
  • assurez-vous de le fermer.

Voici le lien pour comprendre la déclaration «avec»: http://effbot.org/zone/python-with-statement.htm

Edit: Oui, votre utilisation de "avec" est correcte et la fonctionnalité des deux blocs de code est identique. Question sur pourquoi utiliser "avec"? c'est à cause des avantages que vous en retirez. comme vous l'avez mentionné à propos de f.close () manquant accidentellement.

YoK
la source
-4

La manière la plus pythonique pour les codes suivants est:

try:
    f = open("file", "r")
    try:
        line = f.readline()
    finally:
        f.close()
except IOError:
    <whatever>

try:
    f = open("file", "r")
except IOError:
    <whatever>
else:
    f.close()
Leo Liu
la source
1
J'ai ajouté la mise en forme du code pour vous; cela facilite la lecture. Mais vous voudrez peut-être revérifier pour vous assurer que je n'ai pas rompu l'indentation.
andrewsi
2
Non, votre version ne fait pas la même chose que le code d'origine. Même si vous ajoutez l' readline()appel manquant , votre version ne ferme pas le fichier si le readline()résultat est un IOError.
Aleksi Torhamo