Debugging to proces znajdowania i naprawiania błędów (bugów) w kodzie.
Rodzaje błędów:
- Składniowe (Syntax Errors) – błędy w kodzie, program się nie uruchomi
- Runtime Errors – błędy podczas działania (wyjątki)
- 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 diagnostyczneINFO– potwierdzenie że wszystko działaWARNING– coś nieprzewidzianego, ale program działaERROR– poważny problemCRITICAL– 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 w 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ż kodn(next) – następna linias(step) – wejdź do funkcjic(continue) – kontynuuj do następnego breakpointap zmienna– wypisz wartość zmiennejpp zmienna– ładnie wypiszh(help) – pomocq(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
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
startwpackage.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:
- Dodaj print/breakpoint w linii 50
- Sprawdź czy błąd jest przed czy po
- 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:
- Print debugging – proste, szybkie
- Assertions – sprawdzanie założeń
- Logging – profesjonalne logowanie
- PDB/breakpoint() – interaktywny debugger
- 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!