Python - wprowadzenie

17 - Wyjątki - czyli obsługa błędów

Uruchom poniższy kod, wpisz nazwę nieistniejącego pliku.

nazwa_pliku = input('Podaj nazwę pliku: ')
plik_odczyt = open(nazwa_pliku, 'rt')
tekst = plik_odczyt.read()
plik_odczyt.close()
print(tekst)
Podaj nazwę pliku:  przyyklad_1.txt

---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)

<ipython-input-5-92dd2df28933> in <module>
      1 nazwa_pliku = input('Podaj nazwę pliku: ')
----> 2 plik_odczyt = open(nazwa_pliku, 'rt')
      3 tekst = plik_odczyt.read()
      4 plik_odczyt.close()
      5 print(tekst)

FileNotFoundError: [Errno 2] No such file or directory: 'przyyklad_1.txt'

Z błędami, wynikających z różnych powodów, spotykamy się od początków przygody z programowaniem. W powyższym przykładzie, jeśli użytkownik wpisze nazwę nieistniejącego pliku, prowadzi to przerwania działania programu do i wydrukowania komunikatu o błędzie. Przyjrzyjmy się dokładniej, co się właściwie stało.

W przypadku napotkania na błąd, z którym Python nie potrafi sobie poradzić, tworzy obiekt wyjątku. Jeśli nie zadbamy o to, żeby taki wyjątek został odpowiednio obsłużony, działanie programu zostaje przerwane i zostaje wydrukowany stos wywołań, który wskazuje jaki ciąg poleceń doprowadził do błędu i z jakim rodzajem błędu mamy do czynienia, w powyższym przykładzie jest to FileNotFoundError. Analizując komunikat, możemy zwykle znaleźć miejsce i przyczynę błędu, ale sam komunikat nie chroni programu przed ,,wykrzaczeniem się''. Czas zatem nauczyć się, jak taki wyjątek obsłużyć. Uzupełnijmy powyższy kod o konstrukcję try-except:

nazwa_pliku = input('Podaj nazwę pliku: ')
try:     
    plik_odczyt = open(nazwa_pliku, 'rt')
    tekst = plik_odczyt.read()
    plik_odczyt.close()
    print(tekst)
# Kod się wykonuje, w przypadku pojawienia się wyjątku
except: 
    print("Coś poszło nie tak!")
Podaj nazwę pliku:  przyyklad_1.txt

Coś poszło nie tak!

Teraz, mimo że plik nie istnieje, program nie przerwał działania, nie widzieliśmy też komunikatów. Zauważ, że wcześniej Python poinformował nas o rodzaju błędu: FileNotFoundError. Możemy tak zmodyfikować program, aby w przypadku wystąpienia tego właśnie błędu wysłał bardziej precyzyjny komunikat.

nazwa_pliku = input('Podaj nazwę pliku: ')
try:     
    plik_odczyt = open(nazwa_pliku, 'rt')
    tekst = plik_odczyt.read()
    plik_odczyt.close()
    print(tekst)
# Wyjątek w przypadku braku pliku o podanej nazwie
except FileNotFoundError: 
    print(f"Nie ma pliku: {nazwa_pliku}")
Podaj nazwę pliku:  przyyklad_1.txt

Nie ma pliku:  przyyklad_1.txt

Można też przypisać obiekt wyjątku do konkretnej nazwy, np. w celu wyświetlenia informacji o rodzaju błędu. Uzupełnimy kod, przy okazji wprowadzając linię z kodem prowadzącym do powstania innego błędu. Tym razem podaj nazwę istniejącego pliku tekstowego (użyłem pliku z poprzedniej lekcji).

nazwa_pliku = input('Podaj nazwę pliku: ')
try:     
    plik_odczyt = open(nazwa_pliku, 'rt')
    tekst = plik_odczyt.read()
    plik_odczyt.close()
    # Próbujemy wyświetlić ostatni znak, ale podany jest zły indeks
    print(f"Ostatni znak: {tekst[len(tekst)]}")
    # Drukowanie zawartości pliku
    print(tekst)
# Wyjątek w przypadku braku pliku o podanej nazwie
except FileNotFoundError: 
    print(f"Nie ma pliku: {nazwa_pliku}")
# Inny wyjątek
except Exception as wyjatek:
    print(f"Wystąpił błąd: {wyjatek}")    
Podaj nazwę pliku:  przyklad_1.txt

Wystąpił błąd: string index out of range

Gdybyśmy chcieli wydrukować nieco bardziej precyzyjne informacje o miejscu wystąpienia błędu, można zaimportować moduł traceback i dodać odpowiednie polecenie:

import traceback 
nazwa_pliku = input('Podaj nazwę pliku: ')
try:     
    plik_odczyt = open(nazwa_pliku, 'rt')
    tekst = plik_odczyt.read()
    plik_odczyt.close()   
    # Próbujemy wyświetlić ostatni znak, ale podany jest zły indeks
    print(f"Ostatni znak: {tekst[len(tekst)]}")
    # Drukowanie zawartości pliku
    print(tekst)
# Wyjątek w przypadku braku pliku o podanej nazwie
except FileNotFoundError: 
    print(f"Nie ma pliku: {nazwa_pliku}")
# Inny wyjątek
except Exception as wyjatek:
    print(f"Wystąpił błąd: {wyjatek}") 
    # Dodatkowa informacja dotycząca wyjątku:
    traceback.print_exc()
Podaj nazwę pliku:  przyklad_1.txt

Wystąpił błąd: string index out of range

Traceback (most recent call last):
  File "<ipython-input-16-36f670dc8bd0>", line 8, in <module>
    print(f"Ostatni znak: {tekst[len(tekst)]}")
IndexError: string index out of range

Dodatkowy blok else, który możemy dopisać, zawiera kod, który wykonuje się, jeśli nie pojawi się wyjątek w bloku try. Możemy tam przenieść np. linię odpowiedzialną za wydrukowanie zawartości pliku i dopisać dodatkowy komunikat. Naprawimy też linię kodu generującą błąd indeksu.

import traceback 
nazwa_pliku = input('Podaj nazwę pliku: ')
try:     
    plik_odczyt = open(nazwa_pliku, 'rt')
    tekst = plik_odczyt.read()
    plik_odczyt.close()
    # Teraz prawidłowo wyswietla się ostatni znak
    print(f"Ostatni znak: {tekst[len(tekst)-1]}")
        
# Wyjątek w przypadku braku pliku o podanej nazwie
except FileNotFoundError: 
    print(f"Nie ma pliku: {nazwa_pliku}")
# Inny wyjątek
except Exception as wyjatek:
    print(f"Wystąpił błąd: {wyjatek}") 
    # Dodatkowa informacja dotycząca wyjątku:
    traceback.print_exc()
else:
    print(tekst)
    print('Wszystko poszło dobrze!')
Podaj nazwę pliku:  przyklad_1.txt

Ostatni znak: .
Tekst 1.
Tekst 2.
Tekst 3.Tekst 4.
Wszystko poszło dobrze!

Możemy teraz łatwo napisać kod, który będzie prosił użytkownika do skutku (lub rezygnacji) o podanie prawidłowej nazwy pliku:

while True:
    nazwa_pliku = input('Podaj nazwę pliku: ')
    try: 
        plik_odczyt = open(nazwa_pliku, 'rt')
    except FileNotFoundError: 
        print(f"Nie ma pliku: {nazwa_pliku}")
    else:
        tekst = plik_odczyt.read()
        plik_odczyt.close()
        break

print(f"Zawartość pliku {nazwa_pliku}:")
print(tekst)
Podaj nazwę pliku:  przyyklad_1.txt

Nie ma pliku: przyyklad_1.txt

Podaj nazwę pliku:  przyklad_1.txt

Zawartość pliku przyklad_1.txt:
Tekst 1.
Tekst 2.
Tekst 3.Tekst 4.
Last updated on 12 Jan 2021
Published on 12 Jan 2021