12. Python – obsługa błędów i wyjątków


W trakcie wykonywania skryptów, często okazuje się, że natrafiamy na błędy. W takiej sytuacji skrypt kończy swoje działanie, a istnieją sytuacje, w których nie jest to konieczne np. praca na nieistniejącym pliku.

I tutaj z pomocą przychodzą nam wyjątki, które wyświetlają stosowną informację dla użytkownika w przypadku błędu i umożliwiają kontynuację pracy skryptu.

Schemat działania jest standardowy, najpierw próbujemy wykonać kod, a jeżeli coś pójdzie nie tak, to wykonujemy inny.

try: wypróbuj coś co może wywołać błąd
except: wykonaj to jeżeli trafił się błąd
else: wykonaj to jeżeli nie było błędu
finally: wykonaj to nie ważne co się stanie

Błędy jakie możemy napotkać to m.in
 
FileNotFound
 
with open("file.txt") as file:
    file.read()

KeyError
 
some_dictionary = {"key" : "value"}
value = some_dictionary["non_exist_key"]

IndexError
 
some_list = ["PHP", "Python", "JAVA"]
#ang = some_list[3]

TypeError
 
text = "abc"
print(text+5)
 

Zastosowanie przechwytywania błędów

try:
    with open("file.txt") as file:
        file.read()
except:
    with open("file.txt", "w") as file:
      file.write("something")
 
Jednak nie powinniśmy stosować czystego except – powinniśmy wskazać jaki rodzaj błędu chcemy przechwycić, inaczej wszystkie napotkane problemy zostaną rozwiązane w jeden sposób
 
#np. jeżeli zastosujemy poniższy przykład nie otrzymamy żadnego błędu
try:
    with open("file.txt") as file:
        file.read()
    a_dictionary = {"key":"value"}
    print(a_dictionary["jdshbck"])    
except:
    with open("file.txt", "w") as file:
        file.write("something")

Zależy nam na przechwytywaniu konkretnych błędów
try:
  with open("file.txt") as file:
      file.read()
  a_dictionary = {"key":"value"}
    print(a_dictionary["jdshbck"])    
except FileNotFoundError:
  with open("file.txt", "w") as file:
        file.write("something")
 
Po uruchomieniu powyższego kodu zobaczymy błąd: KeyError: 'jdshbck’
naprawmy tą sytuację
 
try:
  with open("file.txt") as file:
      file.read()
  a_dictionary = {"key":"value"}
  print(a_dictionary["jdshbck"])    
except FileNotFoundError:
  with open("file.txt", "w") as file:
      file.write("something")
except KeyError:
    print("Klucz nie zostal znaleziony")

możemy przypisać błąd do zmiennej
try:
  with open("file.txt") as file:
      file.read()
  a_dictionary = {"key":"value"}
  print(a_dictionary["jdshbck"])    
except FileNotFoundError:
  with open("file.txt", "w") as file:
      file.write("something")
except KeyError as error_message:
    print(f"Klucz {error_message} nie zostal znaleziony")

a teraz dodamy blok else, który wykona się jeżeli w bloku try nie będzie błędów
try:
  with open("file.txt") as file:
      content = file.read()
  a_dictionary = {"key":"value"}
  print(a_dictionary["key"])    
except FileNotFoundError:
  with open("file.txt", "w") as file:
      file.write("something")
except KeyError as error_message:
    print(f"Klucz {error_message} nie zostal znaleziony")
else: #blok else nie wykona się jeżeli wystąpi którykolwiek z powyższych błędów
    print(content)

a teraz dodamy blok, który wykona się bez względu na to czy będą błędy, czy też nie
try:
  with open("file.txt") as file:
      content = file.read()
  a_dictionary = {"key":"value"}
  print(a_dictionary["jbfvd"])    
except FileNotFoundError:
  with open("file.txt", "w") as file:
      file.write("something")
except KeyError as error_message:
  print(f"Klucz {error_message} nie zostal znaleziony")
else:
  print(content)
finally:
    print("Jest KeyError, a ja i tak działam")
 
inne przykłady
print(1/0)
try:
  print("Próbujemy dzielić przez 0")
  wynik = 1/0 # to wywoła błąd ZeroDivisionError: division by zero
  print("to się nie wykona")
except Exception:
    print("Wyjątek został przechwycony")
    wynik = 0
print(wynik)

praktyczny przykład – co jeżeli użytkownik wpisze wartość jakiej nie oczekujemy (np literę zamiast liczby)
num = input("Podaj liczbę int")
try:
    num = int(num)
except Exception:
  num = "To nie jest integer"
print(num)

wyświetlimy rodzaj błędu
num = input("Podaj liczbę int: ")
try:
  num = int(num)
except ValueError:
  print(num, "niepoprawny format liczby")
except Exception as e:
  print("Wyjątek został przechwycony: ", type(e))
    num = "To nie jest integer"
print(num)

Własne wyjątki

pomimo poprawnego wykonania kodu możemy narzucić wywołanie błędu np:

try:
  with open("file.txt") as file:
      content = file.read()
  a_dictionary = {"key":"value"}
  print(a_dictionary["key"])    
except FileNotFoundError:
  with open("file.txt", "w") as file:
      file.write("something")
except KeyError as error_message:
  print(f"Klucz {error_message} nie zostal znaleziony")
else:
  print(content)
finally:
    raise TypeError("A i tak masz błąd haha")

Kiedy możemy zastosować powyższe?
Wyobraźcie sobie, że macie kalkulator BMI i ktoś wpisze wzrost powyżej 3m… troszkę wysoki ten ludzik, możemy wtedy narzucić np. ValueError

Wykonaj takie ćwiczenie – stwórz kalkulator BMI i narzuć błąd ValueError

height = float(input("Height: "))
weight = int(input("Weight: "))
if height>3:
  raise ValueError("Wzrost człowieka raczej nie jest większy niż 3m")
bmi = weight / height ** 2
print(bmi)

Zadanie:
Przechwyć wszystkie błędy w poniższym kodzie
fruits = ["Apple", "Pear", "Orange"]
def make_pie(index):
  fruit = fruits[index]
  print(fruit + " pie")
make_pie(4)

Rozwiązanie
fruits = ["Apple", "Pear", "Orange"]
def make_pie(index):
  try:
      fruit = fruits[index-1]
  except IndexError:
      print(f"Nie mamy takiego składnika, wybierz maksymalnie {len(fruits)}")
  else:        
      print(fruit + " pie")
make_pie(3)

Zdanie:
Zapobiegnij błędom
facebook_posts = [
  {'Likes': 21, 'Comments': 2},
  {'Likes': 13, 'Comments': 2, 'Shares': 1},
    {'Likes': 33, 'Comments': 8, 'Shares': 3},
  {'Comments': 4, 'Shares': 2},
  {'Comments': 1, 'Shares': 1},
  {'Likes': 19, 'Comments': 3}
]

total_likes = 0
for post in facebook_posts:
    total_likes = total_likes + post['Likes']
 
Rozwiązanie
facebook_posts = [
  {'Likes': 21, 'Comments': 2},
  {'Likes': 13, 'Comments': 2, 'Shares': 1},
  {'Likes': 33, 'Comments': 8, 'Shares': 3},
    {'Comments': 4, 'Shares': 2},
    {'Comments': 1, 'Shares': 1},
    {'Likes': 19, 'Comments': 3}
]

total_likes = 0

for post in facebook_posts:
  try:
      total_likes = total_likes + post['Likes']
  except KeyError:
      pass
    else:
        print(total_likes)
   
Zadanie: wykonaj program pobierający liczby od użytkownika i dzielący jedną przez drugą. Obsłuż błędy złego formatu, dzielenia przez 0 i pozostałe