Veelvoorkomende valkuilen vermijden: correcte exception handling in Python

Ik zie veel mensen exceptions op de verkeerde manier afhandelen. Misschien geldt dit voor u ook. Komt de volgende situatie bekend voor?
U bent code aan het schrijven, maar u weet dat de bibliotheek die u gebruikt een exception kan raisen. U herinnert u niet precies welke. Op dit punt is het verleidelijk om zogenaamde catch-all blokken te gebruiken en door te gaan met de leuke dingen.
De ergste manier om het te doen
Het ergste wat u kunt doen is een try-except-blok maken dat alles vangt. Met catch-all bedoel ik zoiets als:
try:
...
except:
pass
Catch-all blokken als deze zijn slecht omdat:
- U geen idee heeft welke andere exceptions geraised kunnen worden (meer hierover later).
- We verstoppen de exception door stilzwijgend
passte gebruiken in plaats van de fout te loggen.
Bovendien vangt een lege except alles, inclusief KeyboardInterrupt (control + c), SystemExit, en zelfs NameErrors! Dit betekent dat de volgende code niet netjes gestopt kan worden:
from time import sleep
while True:
try:
print("Probeer me te stoppen")
sleep(1)
except:
print("Stop... niet... me nu!")
Voel u vrij het te proberen. U moet uw terminalvenster sluiten of het Python-proces killen om dit programma te stoppen.
Een iets betere manier om alle exceptions te vangen
In tegenstelling, bij het gebruik van except Exception, hoewel nog steeds een snelle en vuile manier om te veel exceptions te vangen, kunt u tenminste het draaiende proces netjes stoppen:
from time import sleep
while True:
try:
print("Probeer me te stoppen")
sleep(1)
except Exception:
print("Ok, ik stop!")
Wanneer Exception wordt gevangen, vangt u geen SystemExit, KeyboardInterrupt en dergelijke exceptions. Waarom is dat, vraagt u zich af?
Alle exceptions erven van een klasse genaamd BaseException. Volgens de officiële documentatie: "In een try statement met een except clause die een bepaalde klasse noemt, handelt die clause ook alle exception-klassen af die van die klasse afstammen." Een lege except is equivalent aan except BaseException, vandaar dat het alle mogelijke exceptions vangt.
In tegenstelling, de klasse Exception is gedefinieerd als: "Alle built-in, non-system-exiting exceptions zijn afgeleid van deze klasse. Alle door de gebruiker gedefinieerde exceptions zouden ook van deze klasse moeten afstammen."
Het wordt nog erger
In het volgende voorbeeld gebruiken we de os-bibliotheek om de huidige werkmap op te halen. Echter, mijn vette vingers maakten een typfout:
import os
try:
working_dir = os.getcdw()
print(working_dir)
except:
print('fout')
Omdat os.getcdw geen functie is in de os-module, wordt een NameError gegooid. In plaats van te faillen, vangt de except-clause de fout, print 'fout' en het programma gaat door ondanks onze duidelijke typfout. Helaas is dit ook niet oplosbaar door Exception te vangen!
Blijkbaar is ons kleine trucje uit stap één geen fix voor al onze problemen. Wat moeten we dan doen?
Vang wat u kunt afhandelen
Een frase die vaak over exceptions gehoord wordt, is: vang wat je kunt afhandelen. Veel developers zijn geneigd om exceptions direct aan te pakken, terwijl het vaak beter is om de exception door te laten lopen naar een deel van uw programma dat het daadwerkelijk kan afhandelen.
Bijvoorbeeld, overweeg het deel van een teksteditor die bestanden opent en laadt, noemen we het de OpenFile klasse. Als de gebruiker verzoekt een bestand te openen dat niet bestaat, kunt u die fout direct afhandelen, of laten doorlopen.
In dit geval is het beter om de exception door te geven aan de aanroeper, omdat OpenFile geen idee heeft hoe erg deze exception is voor de aanroeper. De aanroeper kan de situatie op meerdere manieren afhandelen:
- Het kan een nieuw bestand met die naam aanmaken en doorgaan
- Misschien heeft de aanroeper het bestand nodig, in welk geval het een foutmelding kan tonen om de gebruiker te informeren dat dit bestand niet bestaat.
Op welke manier dan ook, het is niet aan de OpenFile klasse om te beslissen wat er moet gebeuren in het geval van een FileNotFoundError.
Moet een exception altijd doorgegeven worden? Nee. Een mogelijke exception die in de FileOpen-klasse afgehandeld kan worden, is de TimeoutError. U wilt misschien een paar keer proberen, zonder de aanroeper te lasten met de fout. Dit is een exception die OpenFile kan afhandelen, dus is het OK om het te vangen en opnieuw te proberen.
Conclusie
U zou onder geen omstandigheid meer exceptions moeten vangen dan u kunt afhandelen. Blanket except-blokken zijn een recept voor bugs en onvoorspelbare code. Met andere woorden: vang wat u kunt afhandelen.
Als u uw code schrijft met de 'vang wat je kunt afhandelen' mantra in gedachten, is het schrijven van catch-all-blokken alle regels breken. Dus stop ermee. Als oefening kunt u een deel van uw bestaande code herzien en kijken of het verbeterd kan worden met deze nieuwe kennis!
Python
- CMMS-training meesteren: teams betrekken en retentie verhogen
- Nu Werven? De Stroom Aan Solicitanten Beheersen in een Post-Pandemie Markt
- Python Operators onder de knie krijgen: Een uitgebreide gids
- Python-fouten & ingebouwde exceptions beheersen
- Python-custom exceptions: een praktische gids
- Huidige datum en tijd ophalen in Python: een praktische gids
- Python print()-functie: Praktische handleiding met voorbeelden
- Python-dictionaries: Sleutel/waardeparen toevoegen, bijwerken en beheren
- Python print zonder newline: de end-parameter & andere technieken beheersen
- Gemiddeldes Berekenen in Python: Een Praktische Gids
- Multi-exception handling in Java meesteren