22. Python – Debugging (Debugowanie)


Szybki quiz – przypomnienie poprzedniej lekcji

Pytanie 1. Co zostanie wyświetlone? (klasa Stos z lekcji o Generics)

stos: Stos[int] = Stos()
stos.push(1)
stos.push(2)
stos.push(3)
print(stos.pop())
print(stos.peek())
print(stos.rozmiar())
print(stos)
Odpowiedź

Odpowiedź:

3
2
2
Stos([1, 2])

pop() zdejmuje ze szczytu (LIFO), peek() tylko podgląda, więc po pop() na szczycie jest 2.

Pytanie 2. Metoda pop() w klasie Stos ma typ zwracany Optional[T]. Co zwróci pop() na pustym stosie i co oznacza Optional[T]?

Odpowiedź

Odpowiedź: Zwróci None. Optional[T] oznacza „wartość typu T albo None” (to samo co Union[T, None]).

Pytanie 3. Uzupełnij kod, aby funkcja zwracała element tego samego typu, jaki mają elementy listy:

from typing import List, TypeVar

T = ______('T')

def pierwszy_element(lista: List[T]) -> ______:
    return lista[0]
Odpowiedź

Odpowiedź: TypeVar oraz T.

Pytanie 4. Znajdź błąd. Co się stanie po uruchomieniu i jak to naprawić?

from typing import Generic

class Pudelko(Generic[T]):
    def __init__(self, zawartosc: T):
        self.zawartosc = zawartosc
Odpowiedź

Odpowiedź: NameError: name 'T' is not defined – brakuje importu TypeVar i definicji T = TypeVar('T') przed klasą.

Pytanie 5. Co zostanie wyświetlone?

kolejka: Kolejka[str] = Kolejka()
kolejka.enqueue("A")
kolejka.enqueue("B")
kolejka.enqueue("C")
print(kolejka.dequeue())
print(kolejka)
Odpowiedź

Odpowiedź:

A
Kolejka(['B', 'C'])

Kolejka działa w trybie FIFO – pierwszy dodany element jest pierwszym zdjętym.


Debugging to proces znajdowania i naprawiania błędów (bugów) w kodzie.

Rodzaje błędów:

  1. Składniowe (Syntax Errors) – błędy w kodzie, program się nie uruchomi
  2. Runtime Errors – błędy podczas działania (wyjątki)
  3. Logiczne – program działa, ale wyniki są błędne

Dlaczego debugging jest ważny?

  • Każdy kod ma bugi
  • Debugowanie to 50%+ czasu programisty
  • Umiejętność szybkiego znajdowania błędów = produktywność

Metody debugowania

1. Print debugging (najprostsza)

def oblicz_srednia(liczby):
    suma = 0
    for liczba in liczby:
        print(f"Dodaję: {liczba}")  # Debug
        suma += liczba
    
    print(f"Suma: {suma}")  # Debug
    print(f"Liczba elementów: {len(liczby)}")  # Debug
    
    srednia = suma / len(liczby)
    print(f"Średnia: {srednia}")  # Debug
    
    return srednia

wynik = oblicz_srednia([10, 20, 30])

Zalety:

  • Proste
  • Szybkie
  • Działa wszędzie

Wady:

  • Trzeba pamiętać o usunięciu printów
  • Nieefektywne dla złożonych problemów

2. Asercje (Assertions)

def podziel(a, b):
    assert b != 0, "Dzielnik nie może być zerem!"
    return a / b

# Asercja sprawdza warunek
# Jeśli False → AssertionError z komunikatem

wynik = podziel(10, 2)  # OK
# wynik = podziel(10, 0)  # AssertionError: Dzielnik nie może być zerem!

Użycie:

  • Sprawdzanie założeń
  • Walidacja stanów wewnętrznych
  • Tylko w fazie rozwoju (wyłączane w produkcji)

Python

def oblicz_srednia(liczby):
    assert len(liczby) > 0, "Lista nie może być pusta!"
    assert all(isinstance(x, (int, float)) for x in liczby), "Wszystkie elementy muszą być liczbami!"
    
    return sum(liczby) / len(liczby)

3. Logging – profesjonalne logowanie

import logging

# Konfiguracja
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

def oblicz_srednia(liczby):
    logging.debug(f"Wywołano oblicz_srednia z {liczby}")
    
    if not liczby:
        logging.error("Lista jest pusta!")
        return None
    
    suma = sum(liczby)
    logging.info(f"Suma: {suma}")
    
    srednia = suma / len(liczby)
    logging.info(f"Średnia: {srednia}")
    
    return srednia

wynik = oblicz_srednia([10, 20, 30])

Poziomy logowania:

  • DEBUG – szczegółowe informacje diagnostyczne
  • INFO – potwierdzenie że wszystko działa
  • WARNING – coś nieprzewidzianego, ale program działa
  • ERROR – poważny problem
  • CRITICAL – program może się zatrzymać

4. Debugger PDB (Python Debugger)

PDB to wbudowany debugger w Pythonie.

import pdb

def oblicz_srednia(liczby):
    suma = 0
    
    for liczba in liczby:
        pdb.set_trace()  # ← Breakpoint - program się zatrzyma tutaj
        suma += liczba
    
    return suma / len(liczby)

oblicz_srednia([10, 20, 30])

Podstawowe komendy PDB:

  • l (list) – pokaż kod
  • n (next) – następna linia
  • s (step) – wejdź do funkcji
  • c (continue) – kontynuuj do następnego breakpointa
  • p zmienna – wypisz wartość zmiennej
  • pp zmienna – ładnie wypisz
  • h (help) – pomoc
  • q (quit) – wyjdź

5. Breakpoint() – Python 3.7+

def oblicz_srednia(liczby):
    suma = 0
    
    for liczba in liczby:
        breakpoint()  # ← Nowocześniejszy sposób
        suma += liczba
    
    return suma / len(liczby)

oblicz_srednia([10, 20, 30])

breakpoint() to to samo co pdb.set_trace(), ale lepsze (można łatwo wyłączyć wszystkie).

Debugowanie w IDE

Visual Studio Code

Dokumentacja debuggowania

Interfejs użytkownika debuggera

  • Widok Uruchom i debuguj : wyświetla wszystkie informacje dotyczące uruchamiania, debugowania i zarządzania ustawieniami konfiguracji debugowania.
  • Pasek narzędzi debugowania : zawiera przyciski umożliwiające wykonywanie najczęściej spotykanych czynności debugowania.
  • Konsola debugowania : umożliwia przeglądanie i interakcję z wynikami kodu uruchomionego w debugerze.
  • Pasek boczny debugowania : w trakcie sesji debugowania umożliwia interakcję ze stosem wywołań, punktami przerwania, zmiennymi i zmiennymi obserwowanymi.
  • Menu Uruchom : zawiera najczęstsze polecenia uruchamiania i debugowania.

Rozpocznij sesję debugowania

  • Otwórz plik zawierający kod, który chcesz debugować.
  • Rozpocznij sesję debugowania klawiszem F5 lub wybierz opcję Uruchom i debuguj w widoku Uruchom i debuguj ( workbench.view.debug).

W przypadku bardziej złożonych scenariuszy debugowania, takich jak dołączanie do działającego procesu, należy utworzyć 
launch.jsonplik określający konfigurację debugera. Więcej informacji na temat 
konfiguracji debugowania znajdziesz tutaj .

  • Z listy dostępnych debugerów wybierz debuger, którego chcesz użyć.VS Code próbuje uruchomić i debugować aktualnie aktywny plik. W przypadku Node.js, VS Code sprawdza, czy startw package.jsonpliku znajduje się skrypt, aby określić punkt wejścia aplikacji.
  • Po rozpoczęciu sesji debugowania wyświetlany jest panel DEBUG CONSOLE z wynikami debugowania, a pasek stanu zmienia kolor (domyślny motyw kolorystyczny jest pomarańczowy).

Podstawowe funkcje:

Po rozpoczęciu sesji debugowania u góry okna pojawia się pasek narzędzi Debugowanie. Pasek zawiera akcje sterujące przebiegiem sesji debugowania, takie jak przechodzenie przez kod krok po kroku, wstrzymywanie wykonywania i zatrzymywanie sesji debugowania.

  • F9 – ustaw/usuń breakpoint
  • F5 – rozpocznij debugowanie
  • F10 – następna linia (step over)
  • F11 – wejdź do funkcji (step into)
  • Shift+F11 – wyjdź z funkcji (step out)
  • F5 – kontynuuj

Punkty przerwania

Punkt przerwania to specjalny znacznik, który wstrzymuje wykonywanie kodu w określonym momencie, umożliwiając sprawdzenie stanu aplikacji w tym momencie. VS Code obsługuje kilka typów punktów przerwania

Ustawianie punktów przerwania

Aby ustawić lub usunąć punkt przerwania, kliknij margines edytora lub użyj klawisza F9 w bieżącym wierszu.

  • Punkty przerwania na marginesie edytora są zazwyczaj wyświetlane jako wypełnione na czerwono kółka.
  • Wyłączone punkty przerwania są oznaczone wypełnionym szarym okręgiem.
  • Po uruchomieniu sesji debugowania punkty przerwania, których nie można zarejestrować w debugerze, zmieniają się w szare, puste kółko. To samo może się zdarzyć, jeśli kod źródłowy jest edytowany podczas sesji debugowania bez obsługi edycji na żywo.

Aby mieć większą kontrolę nad punktami przerwania, skorzystaj z sekcji 
„PUNKTY PRZERWANIA” w widoku 
„Uruchom i debuguj” . Ta sekcja zawiera listę wszystkich punktów przerwania w kodzie i udostępnia dodatkowe akcje do zarządzania nimi.

PyCharm

Podobne funkcje:

  • Kliknij na marginesie obok linii → breakpoint
  • Shift+F9 – debuguj
  • F8 – następna linia
  • F7 – wejdź do funkcji

Techniki debugowania

1. Rubber Duck Debugging

Metoda: Wyjaśnij swój kod „gumowej kaczce” (lub koledze) linijka po linijce.

Dlaczego działa: Wymuszając werbalizację często sam znajdziesz błąd.

2. Dziel i zwyciężaj

# Błąd gdzieś w tym kodzie
def skomplikowana_funkcja(dane):
    # 100 linii kodu
    ...
    return wynik

# Podziel na mniejsze części
def czesc1(dane):
    print("Po części 1:", dane)  # Debug
    return wynik1

def czesc2(dane):
    print("Po części 2:", dane)  # Debug
    return wynik2

def czesc3(dane):
    print("Po części 3:", dane)  # Debug
    return wynik3

3. Binary Search Debugging

Jeśli wiesz że błąd jest w 100-liniowej funkcji:

  1. Dodaj print/breakpoint w linii 50
  2. Sprawdź czy błąd jest przed czy po
  3. Powtarzaj aż znajdziesz

4. Zrozum komunikat błędu

Traceback (most recent call last):
  File "program.py", line 10, in <module>
    wynik = oblicz(5, 0)
  File "program.py", line 6, in oblicz
    return a / b
ZeroDivisionError: division by zero

Jak czytać:

  • Od dołu do góry!
  • Ostatnia linia = typ błędu i komunikat
  • Powyżej = stos wywołań (gdzie nastąpił błąd)

Częste błędy i jak je debugować

1. NameError – niezdefiniowana zmienna

print(zmiennna)  # NameError: name 'zmiennna' is not defined

# Debug:
# - Sprawdź literówki
# - Czy zmienna została zdefiniowana?
# - Czy jesteś w odpowiednim zakresie (scope)?

2. IndexError – indeks poza zakresem

lista = [1, 2, 3]
print(lista[5])  # IndexError: list index out of range

# Debug:
print(f"Długość listy: {len(lista)}")
print(f"Próbuję dostać się do indeksu: 5")
# Aha! 5 >= 3

3. KeyError – brak klucza w słowniku

slownik = {"klucz1": "wartość1"}
print(slownik["klucz2"])  # KeyError: 'klucz2'

# Debug:
print(f"Dostępne klucze: {slownik.keys()}")
# Użyj get() zamiast []
print(slownik.get("klucz2", "Domyślna"))

4. TypeError – niewłaściwy typ

wynik = "5" + 5  # TypeError: can only concatenate str (not "int") to str

# Debug:
print(f"Typ pierwszej wartości: {type('5')}")  # str
print(f"Typ drugiej wartości: {type(5)}")      # int
# Konwertuj:
wynik = "5" + str(5)  # lub int("5") + 5

5. AttributeError – brak atrybutu

lista = [1, 2, 3]
lista.add(4)  # AttributeError: 'list' object has no attribute 'add'

# Debug:
print(f"Typ obiektu: {type(lista)}")
print(f"Dostępne metody: {dir(lista)}")
# Poprawka:
lista.append(4)

Narzędzia pomocnicze

1. traceback – szczegółowe informacje o błędzie

import traceback

try:
    wynik = 10 / 0
except Exception as e:
    print("Wystąpił błąd:")
    traceback.print_exc()
    
    # Lub zapisz do pliku
    with open('error.log', 'w') as f:
        traceback.print_exc(file=f)

2. sys.exc_info() – informacje o wyjątku

import sys

try:
    wynik = 10 / 0
except:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    print(f"Typ: {exc_type}")
    print(f"Wartość: {exc_value}")
    print(f"Traceback: {exc_traceback}")

3. inspect – inspekcja obiektów

import inspect

def moja_funkcja(a, b):
    return a + b

# Informacje o funkcji
print(inspect.getsource(moja_funkcja))  # Kod źródłowy
print(inspect.signature(moja_funkcja))  # Sygnatura: (a, b)

4. dis – disassembler

import dis

def dodaj(a, b):
    return a + b

# Zobacz bytecode Pythona
dis.dis(dodaj)

Praktyczne przykłady debugowania

Przykład 1: Błąd logiczny

# Kod ma błąd - znajdź go!
def oblicz_srednia_ocen(oceny):
    suma = 0
    licznik = 0
    
    for ocena in oceny:
        suma += ocena
        licznik += 1
    
    return suma / licznik  # Dzielenie przez zero?

# Test
print(oblicz_srednia_ocen([4, 5, 3]))  # Działa
print(oblicz_srednia_ocen([]))  # ZeroDivisionError!

# Debug z printami
def oblicz_srednia_ocen_debug(oceny):
    print(f"Wejście: {oceny}")
    print(f"Długość: {len(oceny)}")
    
    suma = 0
    licznik = 0
    
    for ocena in oceny:
        suma += ocena
        licznik += 1
        print(f"Suma: {suma}, Licznik: {licznik}")
    
    if licznik == 0:
        print("UWAGA: Dzielenie przez zero!")
        return None
    
    return suma / licznik

# Poprawiona wersja
def oblicz_srednia_ocen_fix(oceny):
    if not oceny:
        return 0  # lub raise ValueError("Lista ocen jest pusta")
    
    return sum(oceny) / len(oceny)

Przykład 2: Debugowanie z PDB

import pdb

def faktoryzacja(n):
    """Zwraca dzielniki liczby n."""
    dzielniki = []
    
    for i in range(1, n + 1):
        pdb.set_trace()  # Zatrzymaj się tutaj
        if n % i == 0:
            dzielniki.append(i)
    
    return dzielniki

print(faktoryzacja(12))

# W PDB:
# p i           # Wypisz i
# p n           # Wypisz n
# p n % i       # Wypisz resztę z dzielenia
# n             # Następna iteracja

Przykład 3: Logging w aplikacji

import logging

# Konfiguracja
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('app.log'),
        logging.StreamHandler()
    ]
)

logger = logging.getLogger(__name__)

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance
        logger.info(f"Utworzono konto dla {owner} z saldem {balance}")
    
    def deposit(self, amount):
        if amount <= 0:
            logger.warning(f"Próba wpłaty nieprawidłowej kwoty: {amount}")
            return False
        
        self.balance += amount
        logger.info(f"{self.owner}: Wpłata {amount}, nowe saldo: {self.balance}")
        return True
    
    def withdraw(self, amount):
        if amount > self.balance:
            logger.error(f"{self.owner}: Próba wypłaty {amount}, ale saldo to tylko {self.balance}")
            return False
        
        self.balance -= amount
        logger.info(f"{self.owner}: Wypłata {amount}, nowe saldo: {self.balance}")
        return True

# Użycie
konto = BankAccount("Jan Kowalski", 1000)
konto.deposit(500)
konto.withdraw(2000)  # Błąd - za mało środków
konto.withdraw(200)

Najlepsze praktyki debugowania

1. Czytaj dokumentację

Przed debugowaniem sprawdź dokumentację funkcji/biblioteki.

2. Pisz testy jednostkowe

import unittest

def dodaj(a, b):
    return a + b

class TestDodaj(unittest.TestCase):
    def test_dodatnie(self):
        self.assertEqual(dodaj(2, 3), 5)
    
    def test_ujemne(self):
        self.assertEqual(dodaj(-2, -3), -5)
    
    def test_mieszane(self):
        self.assertEqual(dodaj(-2, 3), 1)

if __name__ == '__main__':
    unittest.main()

Testy pomagają wykrywać bugi wcześnie.

3. Używaj type hints

def dodaj(a: int, b: int) -> int:
    return a + b

# IDE od razu wykryje błąd
# dodaj("a", "b")  # Typ niezgodny!

4. Code review

Poproś kogoś o przejrzenie kodu – świeże oko często widzi błędy.

5. Git bisect – znajdź kiedy pojawił się bug

git bisect start
git bisect bad  # Obecna wersja ma bug
git bisect good abc123  # Commit abc123 nie miał buga
# Git automatycznie znajdzie commit który wprowadził błąd

Podsumowanie

Metody debugowania:

  1. Print debugging – proste, szybkie
  2. Assertions – sprawdzanie założeń
  3. Logging – profesjonalne logowanie
  4. PDB/breakpoint() – interaktywny debugger
  5. IDE debugger – najpotężniejsze

Techniki:

  • Rubber duck debugging
  • Dziel i zwyciężaj
  • Binary search
  • Czytaj traceback od dołu

Narzędzia:

  • traceback, inspect, dis
  • Testy jednostkowe
  • Type hints

Dobre praktyki:

  • Debuguj systematycznie
  • Dokumentuj znalezione błędy
  • Ucz się na błędach
  • Pisz testy

Pamiętaj: Debugging to umiejętność – im więcej ćwiczysz, tym lepszy się stajesz!

PODSUMOWANIE

Trzy rodzaje błędów:

  • składniowy – Python nie uruchomi programu w ogóle,
  • runtime (wyjątek) – program startuje, ale przerywa działanie w trakcie,
  • logiczny – program działa bez komunikatu błędu, ale wynik jest zły. Ten rodzaj jest najtrudniejszy do znalezienia.

Zasady debugowania:

  • Najpierw czytasz komunikat błędu, dopiero potem zmieniasz kod.
  • Traceback czytasz od dołu: ostatnia linia to typ błędu i komunikat, wyżej jest ścieżka wywołań.
  • Nie zgadujesz – sprawdzasz wartości zmiennych (print, logi, debugger).
  • Szukasz systematycznie: dzielisz kod na części i sprawdzasz, w której części dane przestają być poprawne.

Narzędzia – kiedy które:

  • print – szybkie sprawdzenie, ale trzeba pamiętać o usunięciu go z kodu,
  • assert – sprawdzenie założeń w trakcie tworzenia programu,
  • logging – komunikaty z poziomami ważności, można je zapisywać do pliku,
  • pdb / breakpoint() / debugger w IDE – zatrzymanie programu i oglądanie zmiennych krok po kroku.

Typowe pułapki:

  • zostawione printy i breakpoint() w gotowym kodzie,
  • leczenie objawu zamiast przyczyny (np. „naprawa” błędu przez try/except, który ukrywa problem),
  • poprawianie kilku rzeczy naraz – po zmianie nie wiadomo, co pomogło,
  • czytanie tracebacku od góry zamiast od dołu,
  • brak sprawdzenia przypadków brzegowych (pusta lista, dzielenie przez zero, indeks poza zakresem).

Minimum, które powinno wejść w pamięć

Jeżeli masz zapamiętać tylko kilka rzeczy z tej lekcji, zapamiętaj te:

  1. Trzy rodzaje błędów: składniowy, runtime, logiczny.
  2. Traceback czytasz od dołu – typ błędu i komunikat są w ostatniej linii.
  3. Pięć wyjątków do rozpoznania „z głowy”: NameError, IndexError, KeyError, TypeError, AttributeError (+ ZeroDivisionError).
  4. assert warunek, "komunikat" – gdy warunek jest False, pojawia się AssertionError z komunikatem.
  5. Poziomy logowania od najmniej do najbardziej poważnego: DEBUG, INFO, WARNING, ERROR, CRITICAL.
  6. Zatrzymanie programu w wybranym miejscu: breakpoint() (to samo co pdb.set_trace()), w VS Code – F9.
  7. Komendy PDB: n (następna linia), s (wejdź do funkcji), c (kontynuuj), p zmienna (wypisz), q (wyjdź).
  8. Technika „dziel i zwyciężaj”: sprawdź dane w połowie kodu – jeśli są poprawne, błąd jest dalej; jeśli nie, wcześniej. Powtarzaj.
  9. Zawsze sprawdzaj przypadki brzegowe: pusta lista, zero, indeks poza zakresem, brakujący klucz.
  10. Przed oddaniem kodu usuń printy i breakpoint() użyte do debugowania.

Ściągawka – najważniejsze polecenia i składnia

Rodzaje błędów

RodzajKiedy się ujawniaPrzykład
Składniowyprzed uruchomieniemprint("Witaj"
Runtimew trakcie działania10 / 0
Logicznywynik jest zły, brak komunikatusuma * ile zamiast suma / ile

Najczęstsze wyjątki

WyjątekPrzyczynaCo sprawdzić
NameErrorzmienna nie istniejeliterówka, brak definicji, zakres (scope)
IndexErrorindeks poza listąlen(lista)
KeyErrorbrak klucza w słownikuslownik.keys(), użyj slownik.get(klucz, domyslna)
TypeErrorzły typ (np. "5" + 5)type(x), konwersja str() / int()
AttributeErrorobiekt nie ma takiej metodytype(x), dir(x)
ZeroDivisionErrordzielenie przez zerosprawdź dzielnik przed dzieleniem

Czytanie tracebacku

Traceback (most recent call last):          ← nagłówek
  File "program.py", line 10, in <module>   ← skąd wywołano (wyżej = wcześniej)
    wynik = oblicz(5, 0)
  File "program.py", line 6, in oblicz      ← gdzie wystąpił błąd
    return a / b
ZeroDivisionError: division by zero         ← TYP i komunikat (czytaj od tej linii)

Print debugging

print(f"suma={suma}, i={i}")  # wypisz nazwę i wartość

Asercje

assert len(liczby) > 0, "Lista nie może być pusta!"

Logging

import logging

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

logging.debug("szczegóły diagnostyczne")
logging.info("wszystko działa")
logging.warning("coś nieoczekiwanego, ale program działa")
logging.error("poważny problem")
logging.critical("program może się zatrzymać")

PDB i breakpoint()

breakpoint()          # Python 3.7+
import pdb
pdb.set_trace()       # starsza wersja, to samo działanie
KomendaDziałanie
lpokaż kod
nnastępna linia
swejdź do funkcji
ckontynuuj do następnego breakpointa
p zmienna / pp zmiennawypisz wartość / ładnie wypisz
hpomoc
qwyjdź

Debugger w VS Code

KlawiszDziałanie
F9ustaw / usuń breakpoint
F5start debugowania / kontynuuj
F10następna linia (step over)
F11wejdź do funkcji (step into)
Shift+F11wyjdź z funkcji (step out)

Breakpoint można też ustawić kliknięciem w margines edytora (czerwona kropka). W PyCharm: F8 – następna linia, F7 – wejdź do funkcji.

Techniki

  • Rubber duck – tłumaczysz kod linijka po linijce na głos.
  • Dziel i zwyciężaj – sprawdzasz dane po każdej części kodu.
  • Binary search – print/breakpoint w połowie kodu, potem w połowie podejrzanej części.

Quiz – sprawdź się

Pytanie 1. Określ rodzaj błędu (składniowy / runtime / logiczny):

# a)
print("Witaj"

# b)
x = 10 / 0

# c) liczymy średnią
srednia = suma * len(lista)

Pytanie 2. Co zostanie wyświetlone?

def podziel(a, b):
    assert b != 0, "Dzielnik nie może być zerem!"
    return a / b

print(podziel(10, 4))
print(podziel(5, 0))
print("Koniec")

Pytanie 3. Przeanalizuj komunikat błędu:

Traceback (most recent call last):
  File "program.py", line 10, in <module>
    wynik = oblicz(5, 0)
  File "program.py", line 6, in oblicz
    return a / b
ZeroDivisionError: division by zero

a) Od której linii zaczynasz czytanie? b) Jaki to typ błędu? c) W której funkcji i w której linii wystąpił błąd?

Pytanie 4. Dopasuj każdy fragment kodu do wyjątku, który zgłosi:

# 1
lista = [1, 2, 3]
print(lista[3])

# 2
dane = {"a": 1}
print(dane["b"])

# 3
print("5" + 5)

# 4
lista = [1, 2, 3]
lista.add(4)

# 5
print(wynk)

Wyjątki: NameError, IndexError, KeyError, TypeError, AttributeError.

Pytanie 5. Funkcja ma zwracać sumę liczb parzystych. Program działa bez błędu, ale wynik jest zły. Znajdź błąd i napisz, jak go zlokalizujesz za pomocą print.

def suma_parzystych(liczby):
    suma = 0
    for x in liczby:
        if x % 2 == 1:
            suma += x
    return suma

print(suma_parzystych([1, 2, 3, 4]))   # oczekiwane: 6

Pytanie 6. Dopasuj komunikat do poziomu logowania (DEBUG, INFO, WARNING, ERROR), tak jak w przykładach z lekcji:

a) "Wywołano oblicz_srednia z [10, 20, 30]" b) "Suma: 60" c) "Próba wpłaty nieprawidłowej kwoty: -50" d) "Lista jest pusta!" (funkcja nie może policzyć średniej)

Pytanie 7. Uzupełnij brakujące elementy:

a) Komenda PDB, która przechodzi do następnej linii: ____ b) Komenda PDB, która wypisuje wartość zmiennej i: ____ c) Komenda PDB, która wchodzi do wnętrza wywoływanej funkcji: ____ d) Komenda PDB, która kontynuuje do następnego breakpointa: ____ e) W Pythonie 3.7+ zamiast pdb.set_trace() można wpisać: ____

Pytanie 8. Dopisz jedną linię, aby funkcja zgłaszała AssertionError z komunikatem "Lista nie może być pusta!", gdy dostanie pustą listę:

def pierwszy(lista):
    ______________________
    return lista[0]

Pytanie 9. Debugujesz program w VS Code. Program zatrzymał się na linii wynik = oblicz_srednia(oceny).

a) Który klawisz wykonuje tę linię w całości, bez wchodzenia do funkcji? b) Który klawisz pozwala wejść do wnętrza oblicz_srednia? c) Który klawisz pozwala wyjść z funkcji z powrotem do miejsca wywołania?

Odpowiedzi

1. a) składniowy (brakuje zamykającego nawiasu), b) runtime (ZeroDivisionError), c) logiczny (program działa, ale mnoży zamiast dzielić).

2. Wyświetli się:

2.5

Następnie program przerwie działanie na podziel(5, 0) z komunikatem AssertionError: Dzielnik nie może być zerem!. Napis Koniec nie zostanie wyświetlony.

3. a) Od dołu – od ostatniej linii. b) ZeroDivisionError (dzielenie przez zero). c) W funkcji oblicz, w linii 6 (return a / b); funkcja została wywołana w linii 10 z głównej części programu.

4. 1 – IndexError, 2 – KeyError, 3 – TypeError, 4 – AttributeError, 5 – NameError.

5. Warunek x % 2 == 1 wybiera liczby nieparzyste; powinno być x % 2 == 0. Program zwróci 4 (1 + 3) zamiast 6. Lokalizacja: dodać w pętli np. print(f"x={x}, x % 2={x % 2}, suma={suma}") – widać wtedy, że do sumy trafiają 1 i 3.

6. a) DEBUG, b) INFO, c) WARNING, d) ERROR.

7. a) n, b) p i, c) s, d) c, e) breakpoint().

8. assert len(lista) > 0, "Lista nie może być pusta!" (poprawne jest też assert lista, "Lista nie może być pusta!").

9. a) F10, b) F11, c) Shift+F11.