Pourquoi avons-nous besoin de la clause «enfin» en Python?

306

Je ne sais pas pourquoi nous avons besoin finallyde try...except...finallydéclarations. À mon avis, ce bloc de code

try:
    run_code1()
except TypeError:
    run_code2()
other_code()

est le même avec celui-ci en utilisant finally:

try:
    run_code1()
except TypeError:
    run_code2()
finally:
    other_code()

Suis-je en train de manquer quelque chose?

ARN
la source

Réponses:

422

Cela fait une différence si vous revenez tôt:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   # The finally block is run before the method returns
finally:
    other_code()

Comparez à ceci:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   

other_code()  # This doesn't get run if there's an exception.

Autres situations pouvant entraîner des différences:

  • Si une exception est levée à l'intérieur du bloc except.
  • Si une exception est levée run_code1()mais ce n'est pas le cas TypeError.
  • Autres instructions de flux de contrôle telles que les instructions continueet break.
Mark Byers
la source
1
essayer: #x = Bonjour + 20 x = 10 + 20 sauf: imprimer 'Je suis dans sauf bloc' x = 20 + 30 sinon: imprimer 'Je suis dans autre bloc' x + = 1 enfin: imprimer 'Enfin x =% s '% (x)
Abhijit Sahu
89

Vous pouvez utiliser finallypour vous assurer que les fichiers ou les ressources sont fermés ou libérés, qu'une exception se produise, même si vous ne capturez pas l'exception. (Ou si vous n'attrapez pas cette exception spécifique .)

myfile = open("test.txt", "w")

try:
    myfile.write("the Answer is: ")
    myfile.write(42)   # raises TypeError, which will be propagated to caller
finally:
    myfile.close()     # will be executed before TypeError is propagated

Dans cet exemple, vous feriez mieux d'utiliser l' withinstruction, mais ce type de structure peut être utilisé pour d'autres types de ressources.

Quelques années plus tard, j'ai écrit un article de blog sur un abus de ce finallyque les lecteurs pourraient trouver amusant.

gentil
la source
23

Ils ne sont pas équivalents. Enfin, le code est exécuté quoi qu'il arrive d'autre. Il est utile pour le code de nettoyage qui doit s'exécuter.

Antimoine
la source
15
Finally code is run no matter what else happens... sauf s'il y a une boucle infinie. Ou un powercut. Ou os._exit(). Ou ...
Mark Byers
3
@Mark En fait, sys.exit lève une exception normale. Mais oui, tout ce qui entraîne l'arrêt immédiat du processus signifie que rien d'autre ne s'exécute.
Antimony
1
@Antimony: Merci. Changé en os._exit.
Mark Byers
Je me demande simplement pourquoi le code de nettoyage ne peut pas être placé dans le sauf si le code entrera sauf uniquement si une exception est trouvée?
Stephen Jacob
2
@Stephen D'une part, le code s'exécute enfin même si vous revenez du bloc try. Dans ce cas, vous n'appuyez pas sur la clause except.
Antimony
18

Pour ajouter aux autres réponses ci-dessus, la finallyclause s'exécute quoi qu'il en soit alors que la elseclause ne s'exécute que si aucune exception n'a été levée.

Par exemple, l'écriture dans un fichier sans exception produira ce qui suit:

file = open('test.txt', 'w')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

PRODUCTION:

Writing to file.
Write successful.
File closed.

S'il y a une exception, le code affichera ce qui suit, (notez qu'une erreur délibérée est provoquée en gardant le fichier en lecture seule.

file = open('test.txt', 'r')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

PRODUCTION:

Could not write to file.
File closed.

Nous pouvons voir que la finallyclause s'exécute indépendamment d'une exception. J'espère que cela t'aides.

capitaine noir
la source
2
Cela aurait fonctionné même si vous n'aviez pas utilisé la clause "finalement" qui ne répond pas à la question puisque OP veut connaître la différence, un bon exemple aurait causé une erreur différente de IOError, pour montrer que le enfin le bloc de clause est exécuté avant que l'exception ne soit propagée à l'appelant.
Reda Drissi
2
Je ne savais pas que elsec'était une chose. Utile à savoir.
mazunki
8

Les blocs de code ne sont pas équivalents. La finallyclause sera également exécutée si run_code1()lève une exception autre que TypeError, ou si run_code2()lève une exception, alors que other_code()dans la première version ne serait pas exécutée dans ces cas.

Sven Marnach
la source
7

Dans votre premier exemple, que se passe-t-il si run_code1()déclenche une exception qui ne l'est pas TypeError? ... other_code()ne sera pas exécuté.

Comparez cela avec la finally:version: other_code()est garanti d'être exécuté indépendamment de toute exception levée.

mhawke
la source
7

Comme expliqué dans la documentation , la finallyclause est destinée à définir des actions de nettoyage qui doivent être exécutées en toutes circonstances .

Si finallyest présent, il spécifie un gestionnaire de «nettoyage». La try clause est exécutée, y compris toutes les clauses exceptet else. Si une exception se produit dans l'une des clauses et n'est pas gérée, l'exception est temporairement enregistrée. La finallyclause est exécutée. S'il y a une exception enregistrée, elle est sur-levée à la fin de la finally clause.

Un exemple:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

Comme vous pouvez le voir, la finallyclause est exécutée dans tous les cas. La valeur TypeErrorlevée en divisant deux chaînes n'est pas gérée par la exceptclause et est donc relancée après l' finallyexécution de la clause.

Dans les applications du monde réel, la clause finally est utile pour libérer des ressources externes (telles que des fichiers ou des connexions réseau), que l'utilisation de la ressource ait réussi ou non.

Eugene Yarmash
la source
4

Un exemple parfait est comme ci-dessous:

try:
    #x = Hello + 20
    x = 10 + 20 
except:
    print 'I am in except block'
    x = 20 + 30
else:
    print 'I am in else block'
    x += 1
finally:
    print 'Finally x = %s' %(x)
Abhijit Sahu
la source
3

finallysert à définir des "actions de nettoyage" . La finallyclause est exécutée dans tous les cas avant de quitter l' tryinstruction, qu'une exception (même si vous ne la gérez pas) s'est produite ou non.

J'appuie l'exemple de @ Byers.

kakhkAtion
la source
2

Enfin, vous pouvez également être utilisé lorsque vous souhaitez exécuter du code "facultatif" avant d'exécuter le code pour votre travail principal et que le code facultatif peut échouer pour diverses raisons.

Dans l'exemple suivant, nous ne savons pas exactement quel type d'exceptions store_some_debug_infopeuvent lever.

Nous pourrions exécuter:

try:
  store_some_debug_info()
except Exception:
  pass
do_something_really_important() 

Mais, la plupart des linters se plaindront d'attraper trop vague d'exception. De plus, comme nous choisissons de ne faire que passdes erreurs, le exceptbloc n'ajoute pas vraiment de valeur.

try:
  store_some_debug_info()
finally:
  do_something_really_important()     

Le code ci-dessus a le même effet que le 1er bloc de code mais est plus concis.

Brad Johnson
la source
2

L'utilisation professionnelle de delphi pendant quelques années m'a appris à protéger mes routines de nettoyage en utilisant enfin. Delphi applique à peu près l'utilisation de finalement pour nettoyer toutes les ressources créées avant le bloc try, de peur de provoquer une fuite de mémoire. C'est aussi ainsi que Java, Python et Ruby fonctionnent.

resource = create_resource
try:
  use resource
finally:
  resource.cleanup

et la ressource sera nettoyée indépendamment de ce que vous faites entre essayer et enfin. De plus, il ne sera pas nettoyé si l'exécution n'atteint jamais le trybloc. (c.-à create_resource- d. lui - même lève une exception) Il rend votre code «d'exception sûr».

Quant à savoir pourquoi vous avez réellement besoin d'un bloc enfin, toutes les langues ne le font pas. En C ++ où vous avez automatiquement appelé des destructeurs qui imposent le nettoyage lorsqu'une exception déroule la pile. Je pense que c'est un pas en avant vers un code plus propre que d'essayer ... enfin des langues.

{    
  type object1;
  smart_pointer<type> object1(new type());
} // destructors are automagically called here in LIFO order so no finally required.
nurettin
la source
2

Un bloc try n'a qu'une seule clause obligatoire: l'instruction try. Les clauses except, else et finally sont facultatives et basées sur les préférences de l'utilisateur.

enfin: Avant que Python ne quitte l'instruction try, il exécutera le code dans le bloc finally dans toutes les conditions, même s'il met fin au programme. Par exemple, si Python a rencontré une erreur lors de l'exécution du code dans le bloc except ou else, le bloc finally sera toujours exécuté avant d'arrêter le programme.

Lawrence Krukrubo
la source
1
C'est faux. La déclaration except est obligatoire. - Lucas Azevedo 1 février à 12h04 C'est faux, car je viens de compiler et d'exécuter un programme Python 3.5 avec un bloc try-finally, sans clause "except".
Rob Tow
2
J'ai essayé moi-même et à mon incrédulité, la clause except n'est pas obligatoire.
captainblack
1

Exécutez ces codes Python3 pour observer enfin la nécessité de:

CAS 1:

count = 0
while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")
        finally:
            print("Your Attempts: {}".format(count))

CAS2:

count = 0

while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")

        print("Your Attempts: {}".format(count))

Essayez à chaque fois les entrées suivantes:

  1. entiers aléatoires
  2. code correct qui est 586 (Essayez ceci et vous obtiendrez votre réponse)
  3. chaînes aléatoires

** À un stade très précoce de l'apprentissage de Python.

AshPython
la source
1

J'essayais d'exécuter un code où je voulais lire des feuilles Excel. Le problème était, s'il y a un fichier qui n'a pas de feuille nommée, dites: SheetSum Je ne peux pas le déplacer vers l'emplacement d'erreur !! Le code que j'ai écrit était:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets['df_1'] = pd.read_excel(open(data_file,'rb'), 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

Erreur de don:

[WinError 32] Le processus ne peut pas accéder au fichier car il est utilisé par un autre processus

J'ai dû ajouter un try except with finallybloc complet et dire que finallyje dois fermer le fichier dans tous les cas comme:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets_file = open(data_file,'rb')
        sheets['df_1'] = pd.read_excel(sheets_file, 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    finally:
        sheets_file.close()
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

Sinon, le fichier reste ouvert est l'arrière-plan.

Si finallyest présent, il spécifie un gestionnaire de nettoyage . La try clause est exécutée, y compris toutes les clauses exceptet else. Si une exception se produit dans l'une des clauses et n'est pas gérée, l' exception est temporairement enregistrée . La finallyclause est exécutée. S'il y a une exception enregistrée, elle est sur-levée à la fin de la finally clause. Si la finallyclause lève une autre exception, l'exception enregistrée est définie comme contexte de la nouvelle exception.

..Plus ici

Saqib Mujtaba
la source