Obsługa błędów: pomagamy programom przetrwać

Witaj z powrotem.
W poprzedniej lekcji nauczyłeś się pracy z plikami.
Twoje programy nauczyły się zapisywać dane i czytać je później.
Pisałeś rzeczy takie jak:
with open("tasks.txt", "r") as file:
content = file.read()
Bardzo dobrze.
Teraz twoje programy mogą pamiętać rzeczy.
Zadania.
Notatki.
Kontakty.
Małe kawałki cyfrowej pamięci.
Pięknie.
Ale teraz mamy kolejny problem.
Programy crashują.
Często.
Czasem dlatego, że brakuje pliku.
Czasem dlatego, że użytkownik wpisuje tekst zamiast liczby.
Czasem dlatego, że dzielisz przez zero.
Czasem dlatego, że w kodzie był mały błąd ukryty jak ninja za nawiasem.
Przykład:
age = int(input("Age: "))
Jeśli użytkownik wpisze:
banana
Python powie:
ValueError
I program się wywali.
Bardzo dramatycznie.
Bardzo Python.
Bardzo normalnie.
Obsługa błędów pomaga twojemu programowi przetrwać problemy.
Zamiast crashować, program może odpowiedzieć spokojnie.
Na przykład:
Please enter a valid number.
Dużo lepiej.
Dużo bardziej profesjonalnie.
Dużo mniej “program eksplodował, bo ktoś wpisał banana”.
Czego się nauczysz
W tej lekcji nauczysz się:
- czym są błędy;
- dlaczego programy crashują;
- czym są wyjątki;
- jak używać
try; - jak używać
except; - jak obsługiwać
ValueError; - jak obsługiwać
FileNotFoundError; - jak obsługiwać
ZeroDivisionError; - jak pytać o input ponownie po błędzie;
- jak unikać łapania zbyt wielu błędów;
- jak łączyć obsługę błędów z plikami;
- jak budować bezpieczniejsze programy dla początkujących;
- typowych błędów początkujących przy obsłudze błędów.
Pod koniec tej lekcji twoje programy będą stabilniejsze.
Nie będą od razu panikować.
Będą obsługiwać częste problemy.
Będą potrafiły przetrwać użytkowników.
A użytkownicy są niebezpieczni.
Szczególnie kiedy użytkownikiem jesteś ty o północy.
Czym jest błąd?
Błąd to problem, który przeszkadza programowi działać poprawnie.
Przykład:
number = int("banana")
Python nie może zamienić "banana" na liczbę.
Więc daje błąd:
ValueError
Inny przykład:
result = 10 / 0
Python daje:
ZeroDivisionError
Bo dzielenie przez zero nie jest dozwolone.
Jeszcze inny przykład:
with open("missing.txt", "r") as file:
content = file.read()
Python daje:
FileNotFoundError
Bo plik nie istnieje.
Błędy są normalne.
Nie są dowodem, że jesteś słaby w programowaniu.
Są dowodem, że programowanie to programowanie.
Witaj.
Czym jest wyjątek?
W Pythonie wiele błędów, które pojawiają się podczas działania programu, nazywa się wyjątkami.
Wyjątek pojawia się wtedy, gdy Python napotyka problem w czasie wykonywania programu.
Przykłady:
ValueError
FileNotFoundError
ZeroDivisionError
NameError
TypeError
IndexError
KeyError
Niektóre z nich już widziałeś.
Może nawet za dużo.
To normalne.
Wyjątek zwykle zatrzymuje program.
Chyba że go obsłużysz.
Obsługa błędów oznacza:
Jeśli coś pójdzie źle, zrób coś bezpiecznego zamiast crashować.
Do tego służą try i except.
Bardzo użyteczne.
Bardzo ważne.
Bardzo “proszę, nie eksploduj”.
Twoje pierwsze try i except
Utwórz plik:
error_handling.py
Napisz:
try:
number = int(input("Enter a number: "))
print(f"You entered: {number}")
except ValueError:
print("That was not a valid number.")
Uruchom.
Jeśli wpiszesz:
25
Output:
You entered: 25
Jeśli wpiszesz:
banana
Output:
That was not a valid number.
Program się nie wywala.
Świetnie.
Python próbował uruchomić ryzykowny kod.
Kiedy się nie udało, Python przeskoczył do except.
Bardzo cywilizowanie.
Bardzo użytecznie.
Dużo lepiej niż krzyczeć czerwonym tekstem.
Jak działają try i except
Podstawowa struktura:
try:
risky_code
except SomeError:
code_to_run_if_error_happens
Przykład:
try:
age = int(input("Age: "))
print(age)
except ValueError:
print("Please enter a number.")
Blok try zawiera kod, który może się nie udać.
Blok except zawiera kod, który uruchamia się, jeśli wystąpi wybrany błąd.
Możesz myśleć o tym tak:
Spróbuj tego.
Jeśli pojawi się ten konkretny problem, zrób to zamiast crashować.
To jak siatka bezpieczeństwa.
Bez siatki program spada.
Z siatką program mówi:
Ładna próba, input banana.
I działa dalej.
Obsługa ValueError
ValueError często pojawia się przy konwertowaniu inputu.
Przykład:
age = int(input("Age: "))
Jeśli użytkownik wpisze:
hello
Python nie może zamienić tego na liczbę całkowitą.
Więc zgłasza:
ValueError
Bezpieczniejsza wersja:
try:
age = int(input("Age: "))
print(f"You are {age} years old.")
except ValueError:
print("Age must be a number.")
To bardzo częste.
Input użytkownika jest niebezpieczny.
Użytkownicy wpisują dziwne rzeczy.
Czasem przez pomyłkę.
Czasem dlatego, że testują twój program.
Czasem dlatego, że są chaosem z palcami.
Dlatego zawsze uważaj na input użytkownika.
Pytanie ponownie po błędzie
Zamiast wyświetlić błąd i zakończyć program, możemy zapytać ponownie.
Przykład:
while True:
try:
age = int(input("Age: "))
break
except ValueError:
print("Please enter a valid number.")
print(f"You are {age} years old.")
Jak to działa:
Zacznij nieskończoną pętlę.
Zapytaj o wiek.
Spróbuj przekonwertować.
Jeśli działa, przerwij pętlę.
Jeśli nie działa, pokaż wiadomość i zapytaj ponownie.
Przykład:
Age: banana
Please enter a valid number.
Age: hello
Please enter a valid number.
Age: 25
You are 25 years old.
To dużo lepsze.
Program nie umiera.
Program edukuje użytkownika.
Delikatnie.
Jak cierpliwy nauczyciel.
Ale w środku pewnie jest zmęczony.
Funkcja pomocnicza do bezpiecznego inputu integer
Możemy włożyć tę logikę do funkcji.
Utwórz plik:
safe_input.py
Napisz:
def get_integer(prompt):
while True:
try:
number = int(input(prompt))
return number
except ValueError:
print("Please enter a valid number.")
age = get_integer("Age: ")
print(f"You are {age} years old.")
Teraz get_integer() pyta tak długo, aż użytkownik wpisze poprawną liczbę całkowitą.
To bardzo użyteczne.
Możesz używać tego wiele razy.
Przykład:
quantity = get_integer("Quantity: ")
year = get_integer("Year: ")
score = get_integer("Score: ")
Funkcje plus obsługa błędów.
Bardzo dobrze.
Bardzo czysto.
Bardzo “teraz robimy się poważni”.
Obsługa inputu float
Czasem potrzebujesz liczb dziesiętnych.
Przykład:
price = float(input("Price: "))
To też może zgłosić ValueError.
Bezpieczniejsza funkcja:
def get_float(prompt):
while True:
try:
number = float(input(prompt))
return number
except ValueError:
print("Please enter a valid number.")
price = get_float("Price: ")
print(f"Price: {price:.2f}")
Jeśli użytkownik wpisze:
abc
Program zapyta ponownie.
Jeśli użytkownik wpisze:
19.99
Zadziała.
To przydatne dla:
cen
pomiarów
średnich
zniżek
procentów
Czyli wszystkiego, gdzie liczby dziesiętne wchodzą do pokoju w poważnych butach.
Obsługa ZeroDivisionError
Dzielenie przez zero nie jest dozwolone.
Przykład:
result = 10 / 0
Python daje:
ZeroDivisionError
Bezpieczniejszy przykład:
try:
number = float(input("Number: "))
result = 100 / number
print(f"Result: {result}")
except ZeroDivisionError:
print("You cannot divide by zero.")
except ValueError:
print("Please enter a valid number.")
Jeśli użytkownik wpisze:
0
Output:
You cannot divide by zero.
Jeśli użytkownik wpisze:
banana
Output:
Please enter a valid number.
To obsługuje dwa możliwe problemy.
Bardzo praktyczne.
Bardzo użyteczne.
Bardzo “program już trochę życia widział”.
Wiele bloków except
Możesz obsługiwać różne błędy osobno.
Przykład:
try:
number = int(input("Enter a number: "))
result = 100 / number
print(result)
except ValueError:
print("That was not a valid number.")
except ZeroDivisionError:
print("You cannot divide by zero.")
To dobre, bo różne błędy potrzebują różnych komunikatów.
ValueError oznacza:
Input nie był liczbą.
ZeroDivisionError oznacza:
Liczba była zerem.
Inny problem.
Inna odpowiedź.
Dobre programy jasno tłumaczą problemy.
Złe programy mówią:
Something went wrong.
A potem znikają we mgle.
Nie bądź mgłą.
Obsługa FileNotFoundError
Widziałeś już pliki.
Czytanie brakującego pliku powoduje FileNotFoundError.
Przykład:
try:
with open("notes.txt", "r") as file:
content = file.read()
print(content)
except FileNotFoundError:
print("The file does not exist yet.")
Jeśli notes.txt istnieje, program go czyta.
Jeśli nie istnieje, program wypisuje:
The file does not exist yet.
To dużo lepsze niż crash.
Błędy plików są częste.
Szczególnie kiedy:
plik nigdy nie został utworzony
nazwa pliku jest zła
plik jest w innym folderze
uruchamiasz Pythona z niewłaściwego katalogu
Klasyczny dramat plików.
Bardzo normalny.
Bardzo naprawialny.
Bezpieczne ładowanie zadań
W poprzedniej lekcji zapisywaliśmy zadania w pliku.
Teraz możemy ładować je bezpiecznie.
Przykład:
def load_tasks():
tasks = []
try:
with open("tasks.txt", "r") as file:
for line in file:
tasks.append(line.strip())
except FileNotFoundError:
pass
return tasks
Jeśli plik istnieje, zadania są ładowane.
Jeśli plik nie istnieje, funkcja zwraca pustą listę.
To dobre.
Przy pierwszym uruchomieniu programu pliku może jeszcze nie być.
To nie katastrofa.
To tylko nowy program bez danych.
Bardzo niewinny.
Bardzo pusty.
Bardzo gotowy, żeby później stać się bałaganem.
Używanie else z try
Python wspiera też else z try.
Blok else uruchamia się tylko wtedy, gdy nie ma błędu.
Przykład:
try:
number = int(input("Enter a number: "))
except ValueError:
print("Invalid number.")
else:
print(f"Good number: {number}")
Jeśli input jest poprawny:
Enter a number: 10
Good number: 10
Jeśli input jest niepoprawny:
Enter a number: banana
Invalid number.
Blok else jest opcjonalny.
Nie zawsze go potrzebujesz.
Ale czasem sprawia, że kod jest czytelniejszy.
Prosta idea:
try — spróbuj ryzykowną rzecz
except — jeśli się nie uda
else — jeśli się uda
Bardzo schludnie.
Bardzo Python.
Jak mała umowa prawna dla błędów.
Używanie finally
Python ma też finally.
Blok finally uruchamia się zawsze.
Przykład:
try:
number = int(input("Enter a number: "))
print(number)
except ValueError:
print("Invalid number.")
finally:
print("Program finished.")
Jeśli użytkownik wpisze liczbę, finally się uruchomi.
Jeśli użytkownik wpisze zły input, finally też się uruchomi.
Przykład:
Enter a number: banana
Invalid number.
Program finished.
finally jest przydatne, kiedy trzeba coś posprzątać.
W programach dla początkujących nie będziesz go często potrzebować.
Ale dobrze wiedzieć, że istnieje.
Jak gaśnica.
Nie używasz jej codziennie.
Ale chcesz, żeby była.
Nie łap wszystkiego zbyt wcześnie
Możesz zobaczyć kod taki jak:
try:
number = int(input("Number: "))
except:
print("Something went wrong.")
To łapie każdy błąd.
Działa.
Ale zwykle nie jest idealne.
Dlaczego?
Bo może ukrywać prawdziwe bugi.
Lepiej:
try:
number = int(input("Number: "))
except ValueError:
print("Please enter a valid number.")
To obsługuje konkretny problem, którego się spodziewasz.
Konkretna obsługa błędów jest lepsza.
Mówi, co naprawdę się stało.
Łapanie wszystkiego jest jak przykrycie problemu kocem.
Problem nadal tam jest.
Teraz jest tylko cieplejszy.
I trudniejszy do zobaczenia.
Nie wkładaj zbyt dużo kodu do try
Źle:
try:
name = input("Name: ")
age = int(input("Age: "))
city = input("City: ")
print(user_name)
except ValueError:
print("Invalid age.")
To ryzykowne, bo blok try zawiera za dużo.
Mogą tam być inne błędy.
Na przykład:
print(user_name)
może spowodować NameError.
Lepiej:
name = input("Name: ")
try:
age = int(input("Age: "))
except ValueError:
print("Invalid age.")
city = input("City: ")
Trzymaj try skupione na kodzie, który może wywołać spodziewany błąd.
To ułatwia debugging.
A łatwiejszy debugging to szczęście.
Małe szczęście.
Ale nadal szczęście.
Error handling nie służy do ukrywania zepsutego kodu
Ważna lekcja.
Obsługa błędów nie jest koszem na zepsutą logikę.
Zły pomysł:
try:
broken_code_here
except:
pass
To ukrywa problemy.
Program może działać dalej, ale coś jest źle.
A teraz nie wiesz co.
except: pass może być użyteczne w rzadkich przypadkach.
Ale początkujący nie powinni używać go lekko.
Jeśli ignorujesz każdy błąd, twój program staje się tajemniczym stworzeniem.
Działa.
Może.
Robi rzeczy.
Może.
Nikt nie wie dlaczego.
To nie inżynieria.
To nawiedzone programowanie.
Unikaj.
Mini program: bezpieczny input wieku
Utwórz plik:
safe_age.py
Napisz:
while True:
try:
age = int(input("Age: "))
break
except ValueError:
print("Please enter a valid age.")
print(f"Your age is {age}.")
Przykład:
Age: hello
Please enter a valid age.
Age: 33
Your age is 33.
Ten program pyta tak długo, aż użytkownik wpisze poprawną liczbę całkowitą.
Prosto.
Użytecznie.
Dużo lepiej niż crash.
To bardzo częsty pattern dla początkujących.
Używaj go często.
Twoi użytkownicy będą wpisywać dziwne rzeczy.
Twój program nie powinien mdleć.
Mini program: bezpieczny kalkulator
Utwórz plik:
safe_calculator.py
Napisz:
try:
first_number = float(input("First number: "))
second_number = float(input("Second number: "))
result = first_number / second_number
print(f"Result: {result:.2f}")
except ValueError:
print("Please enter valid numbers.")
except ZeroDivisionError:
print("You cannot divide by zero.")
Przykład:
First number: 10
Second number: 2
Result: 5.00
Jeśli użytkownik wpisze:
Second number: 0
Output:
You cannot divide by zero.
Ten program obsługuje dwa częste problemy.
Niepoprawny input.
Dzielenie przez zero.
Bardzo użyteczne.
Bardzo praktyczne.
Bardzo “kalkulator ma teraz instynkt przetrwania”.
Mini program: bezpieczny czytnik notatek
Utwórz plik:
safe_notes_reader.py
Napisz:
try:
with open("notes.txt", "r") as file:
print("Notes:")
for line in file:
print(f"- {line.strip()}")
except FileNotFoundError:
print("No notes found yet.")
Jeśli plik istnieje, pokazuje notatki.
Jeśli nie istnieje, wypisuje:
No notes found yet.
To jest czyste.
Brakujący plik nie zawsze jest katastrofą.
Czasem oznacza po prostu:
Jeszcze nie ma danych.
To normalna sytuacja.
Dobre programy rozumieją normalne sytuacje.
Złe programy panikują i rzucają czerwonym tekstem.
Wolimy spokojne programy.
Bardzo Zen.
Bardzo Python.
Mini program: bezpieczniejszy task manager
Teraz ulepszmy task manager.
Utwórz plik:
safe_task_manager.py
Napisz:
FILE_NAME = "tasks.txt"
def load_tasks():
tasks = []
try:
with open(FILE_NAME, "r") as file:
for line in file:
tasks.append(line.strip())
except FileNotFoundError:
pass
return tasks
def save_tasks(tasks):
with open(FILE_NAME, "w") as file:
for task in tasks:
file.write(task + "\n")
def show_menu():
print("----- Task Manager -----")
print("1. Add task")
print("2. Show tasks")
print("3. Remove task")
print("q. Quit")
def add_task(tasks):
task = input("Task: ")
tasks.append(task)
save_tasks(tasks)
print("Task saved.")
def show_tasks(tasks):
if len(tasks) == 0:
print("No tasks yet.")
else:
print("Tasks:")
for index, task in enumerate(tasks, start=1):
print(f"{index}. {task}")
def remove_task(tasks):
if len(tasks) == 0:
print("No tasks to remove.")
return
show_tasks(tasks)
try:
task_number = int(input("Task number to remove: "))
index = task_number - 1
if index < 0 or index >= len(tasks):
print("Invalid task number.")
return
removed_task = tasks.pop(index)
save_tasks(tasks)
print(f"Removed: {removed_task}")
except ValueError:
print("Please enter a valid number.")
tasks = load_tasks()
while True:
show_menu()
choice = input("Choose an option: ").lower()
if choice == "1":
add_task(tasks)
elif choice == "2":
show_tasks(tasks)
elif choice == "3":
remove_task(tasks)
elif choice == "q":
print("Goodbye.")
break
else:
print("Unknown option.")
To mocniejszy program.
Używa:
- plików;
- funkcji;
- list;
- pętli;
try;except;ValueError;FileNotFoundError;enumerate();- bezpiecznego usuwania zadań.
To prawdziwe beginner software.
Małe, tak.
Ale prawdziwe.
Zapisuje dane.
Ładuje dane.
Obsługuje brakujące pliki.
Obsługuje zły input.
Tak rosną programy.
Jedna siatka bezpieczeństwa naraz.
Co robi enumerate()
W task managerze użyliśmy:
for index, task in enumerate(tasks, start=1):
print(f"{index}. {task}")
enumerate() daje jednocześnie:
numer
element
Przykład:
tasks = ["Buy milk", "Study Python"]
for index, task in enumerate(tasks, start=1):
print(f"{index}. {task}")
Output:
1. Buy milk
2. Study Python
To przydatne, kiedy pokazujesz numerowane listy.
Użytkownik widzi zadanie numer 1.
Pythonowy index listy to tak naprawdę 0.
Więc przy usuwaniu zadania robimy:
index = task_number - 1
Numer przyjazny użytkownikowi.
Index przyjazny Pythonowi.
Dyplomacja.
Między ludźmi i maszynami.
Bardzo ważne.
Typowy błąd: obsługa złego błędu
Przykład:
try:
age = int(input("Age: "))
except FileNotFoundError:
print("File not found.")
To nie ma sensu.
Ryzykowny kod konwertuje input do int.
Prawdopodobny błąd to:
ValueError
Poprawnie:
try:
age = int(input("Age: "))
except ValueError:
print("Invalid age.")
Obsługuj błąd, który naprawdę może się wydarzyć.
Inaczej program pilnuje złych drzwi.
A bug wchodzi przez okno.
Klasyczne zachowanie buga.
Typowy błąd: zbyt ogólne except
Nieidealnie:
try:
age = int(input("Age: "))
except:
print("Something went wrong.")
Lepiej:
try:
age = int(input("Age: "))
except ValueError:
print("Please enter a valid age.")
Konkret jest lepszy.
Im bardziej konkretna obsługa błędów, tym łatwiejszy debugging.
Niejasny komunikat nikomu nie pomaga.
Szczególnie tobie.
A to ty musisz naprawić kod.
Bądź miły dla przyszłego siebie.
Przyszły ty już wystarczająco cierpiał.
Typowy błąd: całkowite ignorowanie błędu
Niebezpiecznie:
try:
age = int(input("Age: "))
except ValueError:
pass
Jeśli użytkownik wpisze zły input, nic się nie dzieje.
Program po cichu ignoruje problem.
To może tworzyć mylące bugi.
Lepiej:
try:
age = int(input("Age: "))
except ValueError:
print("Please enter a valid age.")
Przynajmniej powiedz użytkownikowi, co się stało.
Ciche porażki są niebezpieczne.
To jak alarm przeciwpożarowy, który szepcze.
Niezbyt użyteczne.
Typowy błąd: używanie zmiennej, która nie została utworzona
Przykład:
try:
age = int(input("Age: "))
except ValueError:
print("Invalid age.")
print(age)
Jeśli użytkownik wpisze niepoprawny input, age nigdy nie zostanie utworzone.
Potem:
print(age)
może spowodować:
NameError
Lepiej:
try:
age = int(input("Age: "))
print(age)
except ValueError:
print("Invalid age.")
Albo użyj pętli, dopóki input nie będzie poprawny.
Bardzo ważne.
Jeśli zmienna jest tworzona w bloku try, upewnij się, że istnieje, zanim użyjesz jej później.
Python nie jest magią.
Nie wymyśli zmiennej z litości.
Typowy błąd: obsługa błędów bez naprawienia flow
Źle:
try:
number = int(input("Number: "))
except ValueError:
print("Invalid number.")
result = number * 2
print(result)
Jeśli input jest niepoprawny, number może nie istnieć.
Lepiej:
while True:
try:
number = int(input("Number: "))
break
except ValueError:
print("Invalid number.")
result = number * 2
print(result)
Teraz program kontynuuje tylko wtedy, gdy number jest poprawny.
To dobry flow.
Obsługa błędów nie polega tylko na wypisywaniu komunikatów.
Chodzi o kontrolowanie tego, co dzieje się dalej.
Bardzo ważne.
Bardzo architektura programu.
Mała architektura.
Ale nadal architektura.
Praktyka
Utwórz plik:
practice_error_handling.py
Napisz program, który:
- pyta użytkownika o cenę;
- pyta użytkownika o ilość;
- oblicza total;
- obsługuje niepoprawny input;
- pyta tak długo, aż input będzie poprawny.
Przykładowe rozwiązanie:
def get_float(prompt):
while True:
try:
value = float(input(prompt))
return value
except ValueError:
print("Please enter a valid number.")
def get_integer(prompt):
while True:
try:
value = int(input(prompt))
return value
except ValueError:
print("Please enter a valid whole number.")
price = get_float("Price: ")
quantity = get_integer("Quantity: ")
total = price * quantity
print(f"Total: {total:.2f}")
Przykład:
Price: hello
Please enter a valid number.
Price: 10.50
Quantity: banana
Please enter a valid whole number.
Quantity: 3
Total: 31.50
To czysty program.
Obsługuje zły input.
Używa funkcji ponownie.
Liczy bezpiecznie.
Bardzo dobra praktyka.
Bardzo sklepowe.
Podatki nadal czają się niedaleko.
Ale nie dzisiaj.
Mini wyzwanie
Utwórz plik:
safe_contact_book.py
Zbuduj małą książkę kontaktów, która:
- zapisuje kontakty do
contacts.txt; - ładuje kontakty z pliku;
- pozwala użytkownikowi dodawać kontakty;
- pozwala użytkownikowi pokazywać kontakty;
- bezpiecznie obsługuje brakujący plik;
- obsługuje złe opcje menu;
- nie crashuje przy pierwszym uruchomieniu.
Prosty format kontaktu:
name,email,phone
Przykładowa struktura:
FILE_NAME = "contacts.txt"
def load_contacts():
contacts = []
try:
with open(FILE_NAME, "r") as file:
for line in file:
parts = line.strip().split(",")
if len(parts) == 3:
contact = {
"name": parts[0],
"email": parts[1],
"phone": parts[2]
}
contacts.append(contact)
except FileNotFoundError:
pass
return contacts
def save_contacts(contacts):
with open(FILE_NAME, "w") as file:
for contact in contacts:
file.write(f"{contact['name']},{contact['email']},{contact['phone']}\n")
def add_contact(contacts):
contact = {
"name": input("Name: "),
"email": input("Email: "),
"phone": input("Phone: ")
}
contacts.append(contact)
save_contacts(contacts)
print("Contact saved.")
def show_contacts(contacts):
if len(contacts) == 0:
print("No contacts yet.")
else:
print("Contacts:")
for contact in contacts:
print("-----")
print(f"Name: {contact['name']}")
print(f"Email: {contact['email']}")
print(f"Phone: {contact['phone']}")
def show_menu():
print("----- Contact Book -----")
print("1. Add contact")
print("2. Show contacts")
print("q. Quit")
contacts = load_contacts()
while True:
show_menu()
choice = input("Choose an option: ").lower()
if choice == "1":
add_contact(contacts)
elif choice == "2":
show_contacts(contacts)
elif choice == "q":
print("Goodbye.")
break
else:
print("Unknown option.")
Ten program łączy wiele rzeczy, których się nauczyłeś:
- zmienne;
- stringi;
- listy;
- słowniki;
- pętle;
- warunki;
- funkcje;
- pliki;
- podstawową obsługę błędów.
To dużo.
To już nie jest tylko “Hello, World!”.
To mały prawdziwy program.
Zapisuje dane.
Ładuje dane.
Używa strukturalnych informacji.
Obsługuje brakujące pliki.
Bardzo mocny postęp.
Bardzo Python.
Checklist dla początkujących
Kiedy twoja obsługa błędów nie działa, sprawdź:
Czy włożyłem ryzykowny kod do try?
Czy łapię poprawny błąd?
Czy użyłem ValueError dla niepoprawnej konwersji liczby?
Czy użyłem FileNotFoundError dla brakujących plików?
Czy użyłem ZeroDivisionError dla dzielenia przez zero?
Czy przypadkiem łapię wszystko przez gołe except?
Czy ukrywam błędy przez pass?
Czy program bezpiecznie kontynuuje po błędzie?
Czy zmienna może nie istnieć po nieudanym try?
Czy powinienem użyć pętli, żeby zapytać ponownie?
Czy mój blok try jest za duży?
Obsługa błędów nie polega na tym, żeby błędy zniknęły.
Polega na tym, żeby odpowiedzieć poprawnie.
Dobry program nie udaje, że problemy nie istnieją.
Dobry program mówi:
Spodziewałem się tego problemu.
Oto co robimy.
Bardzo spokojnie.
Bardzo dorosło.
Bardzo użytecznie.
Podsumowanie
Dzisiaj nauczyłeś się:
- błędy są normalne w programowaniu;
- wyjątki pojawiają się, gdy Python napotyka problemy w czasie działania;
tryzawiera ryzykowny kod;exceptobsługuje wybrane błędy;ValueErrorczęsto pojawia się przy niepoprawnej konwersji liczby;FileNotFoundErrorpojawia się przy czytaniu brakujących plików;ZeroDivisionErrorpojawia się przy dzieleniu przez zero;- pętle mogą pytać o input ponownie po błędzie;
- funkcje pomocnicze mogą robić bezpieczny input wielokrotnego użytku;
- wiele bloków
exceptmoże obsługiwać różne błędy; elseuruchamia się, gdy nie ma błędu;finallyuruchamia się zawsze;- łapanie każdego błędu może ukrywać bugi;
- bloki
tryzwykle powinny być skupione; - obsługa błędów powinna poprawiać flow programu, a nie ukrywać zepsutą logikę.
To duży krok.
Twoje programy są teraz bezpieczniejsze.
Mogą obsłużyć zły input.
Mogą obsłużyć brakujące pliki.
Mogą uniknąć teatralnych crashy.
Mogą prowadzić użytkownika zamiast eksplodować.
Tego potrzebują prawdziwe programy.
Bo prawdziwe życie jest chaotyczne.
Pliki znikają.
Użytkownicy wpisują banana.
Liczby stają się zerem.
A czasem zapominasz, co napisałeś wczoraj.
Obsługa błędów pomaga twojemu programowi przetrwać ten chaos.
Bardzo użyteczne.
Bardzo Python.
Bardzo realne.
Następna lekcja
W następnej lekcji nauczymy się pracy z modułami i importami.
Nauczysz się dzielić kod na różne pliki i używać kodu ponownie w projekcie.
Zamiast trzymać wszystko w jednym ogromnym pliku, zorganizujesz kod tak:
main.py
helpers.py
calculator.py
tasks.py
Potem będziesz importować funkcje z jednego pliku do drugiego.
To kolejny ogromny krok w stronę prawdziwych projektów.
Bo prawdziwe projekty nie są jednym gigantycznym plikiem.
Zwykle.
Chyba że ktoś cierpiał.
A tego nie chcemy.
Chcemy czystej struktury.
Kodu wielokrotnego użytku.
I mniej plików nazwanych final_final_really_final.py.
Bardzo Python.
Bardzo następny poziom.