Last modified: June 06, 2026

This article is written in: 🇵🇱

Debugowanie

Debugowanie to fundamentalny proces w tworzeniu oprogramowania, polegający na identyfikowaniu, analizowaniu i usuwaniu błędów (bugów) w kodzie źródłowym programu. Błędy te mogą prowadzić do nieprawidłowego działania aplikacji, awarii systemu lub nieoczekiwanych rezultatów. Debugowanie umożliwia programistom dokładne prześledzenie działania programu, co pozwala na zrozumienie, dlaczego program nie działa zgodnie z oczekiwaniami.

Proces ten jest niezbędny na każdym etapie cyklu życia oprogramowania, od fazy rozwoju po utrzymanie i aktualizacje. Dzięki debugowaniu programiści mogą:

Główne funkcje debuggera

Debugger to narzędzie oferujące zaawansowane funkcje ułatwiające analizę i naprawę kodu. Oto szczegółowy opis jego głównych funkcji:

Punkty przerwania (breakpoints):

Krokowanie (stepping):

Inspekcja stanu:

Modyfikacja stanu:

Zalety korzystania z debuggera

Korzystanie z debuggera przynosi wiele korzyści, zarówno dla początkujących, jak i doświadczonych programistów:

Debugger w środowiskach programistycznych (IDE)

Zintegrowane środowiska programistyczne (IDE) oferują zaawansowane narzędzia debugowania, które są nieodłączną częścią nowoczesnego procesu tworzenia oprogramowania. Oto niektóre z ich funkcji:

Debugger wbudowany w Pythonie - pdb

pdb jest wbudowanym debuggerem Pythona, który, mimo swojej prostoty, oferuje ma wiele do zaoferowania:

Uruchomienie debuggera w kodzie

Istnieje kilka sposobów na rozpoczęcie sesji debugowania z użyciem pdb:

Bezpośrednie wywołanie z linii poleceń:

python -m pdb script.py

Wstawienie punktu przerwania w kodzie:

Od Pythona 3.7 można użyć wbudowanej funkcji:

breakpoint()  # Automatycznie uruchamia domyślny debugger

Lub tradycyjnie:

import pdb
pdb.set_trace()

Debugowanie wyjątków:

Aby debugger uruchamiał się automatycznie przy nieobsłużonych wyjątkach:

import sys
import pdb

def excepthook(type, value, traceback):
    pdb.post_mortem(traceback)

sys.excepthook = excepthook

Kontrolowanie wykonania programu w pdb

Podczas sesji z pdb masz dostęp do szeregu poleceń:

Nawigacja:

Informacje o stanie:

Zarządzanie breakpointami:

Pomoc i wyjście:

Podgląd i edycja zmiennych w pdb

pdb pozwala na interaktywną pracę z kodem:

Wyświetlanie wartości:

p variable_name  # Wyświetla wartość zmiennej

Zmiana wartości:

variable_name = new_value  # Ustawia nową wartość zmiennej

Wykonywanie wyrażeń:

Możesz wykonywać dowolne wyrażenia Pythona:

p len(my_list)  # Wyświetla długość listy

Wywoływanie funkcji:

Uważaj jednak na skutki uboczne wywoływanych funkcji:

p my_function()  # Wywołuje funkcję i wyświetla jej wynik

Ustawianie dodatkowych punktów zatrzymania

Podczas debugowania możesz potrzebować zatrzymać program w nowych miejscach:

Ustawianie breakpointów:

b 25  # Ustawia breakpoint na linii 25 bieżącego pliku
b my_module.py:10  # Ustawia breakpoint w pliku 'my_module.py' na linii 10
b my_function  # Ustawia breakpoint na początku funkcji 'my_function'

Warunkowe breakpointy:

b 30, x > 5  # Zatrzyma program na linii 30, gdy 'x' jest większe od 5

Wyświetlanie breakpointów:

b  # Wyświetla listę wszystkich breakpointów

Usuwanie breakpointów:

cl  # Usuwa wszystkie breakpointy
cl 2  # Usuwa breakpoint numer 2

Warunkowe zatrzymywanie

Warunkowe breakpointy są niezwykle przydatne w sytuacjach, gdy błąd występuje tylko przy określonych wartościach zmiennych lub w specyficznych warunkach.

Przykład zastosowania:

Jeśli pętla iteruje 1000 razy, ale błąd występuje tylko dla i == 500, ustawienie breakpointa warunkowego pozwala na zatrzymanie programu dokładnie w tym momencie:

b loop.py:45, i == 500

Debugowanie złożonych warunków:

Możesz używać złożonych wyrażeń logicznych:

b process_data, len(data) > 100 and error_flag

Praktyczne wskazówki

Techniki debugowania bez debuggera

Często najszybsze debugowanie odbywa się bez uruchamiania debuggera. Oto sprawdzone techniki:

Najbardziej podstawowa technika — wypisanie stanu zmiennych:

from pprint import pprint

# Zwykłe print z etykietą
print(f"[DEBUG] lista={lista}, suma={suma}")

# pprint — czytelne wydrukowanie złożonych struktur
pprint({"klucz": [1, 2, 3], "zagniezdzone": {"a": 1}})

# Inspekcja obiektu
class Konfiguracja:
    host = "localhost"
    port = 8080

cfg = Konfiguracja()
print(vars(cfg))       # {'host': 'localhost', 'port': 8080}
print(dir(cfg))        # lista wszystkich atrybutów i metod

Moduł logging zamiast print()

W produkcyjnym kodzie zamiast print() używamy logging:

import logging

logging.basicConfig(level=logging.DEBUG,
                    format="%(levelname)s:%(funcName)s:%(lineno)d: %(message)s")

def oblicz(a, b):
    logging.debug(f"oblicz wywołana z a={a}, b={b}")
    wynik = a / b
    logging.debug(f"wynik: {wynik}")
    return wynik

assert — szybkie weryfikowanie założeń

def sortuj_posortowane(lista):
    assert isinstance(lista, list), f"Oczekiwano listy, otrzymano {type(lista)}"
    assert len(lista) > 0, "Lista nie może być pusta"
    wynik = sorted(lista)
    assert wynik[0] <= wynik[-1], "Błąd sortowania!"
    return wynik

Uwaga: assert jest wyłączane gdy Python jest uruchamiany z flagą -O (optymalizacja). Nie używaj do sprawdzania warunków krytycznych dla bezpieczeństwa — używaj if ... raise.

traceback — szczegółowe ślady błędów

import traceback

try:
    x = 1 / 0
except ZeroDivisionError:
    traceback.print_exc()   # Wydrukuj pełny traceback
    # lub
    tb_str = traceback.format_exc()   # Pobierz jako string do logu
    logging.error("Błąd: %s", tb_str)

Profilowanie kodu (cProfile, timeit)

Profilowanie pozwala znaleźć wolne miejsca w kodzie:

import cProfile
import pstats

# Profilowanie funkcji
def wolna_funkcja():
    return sum(i**2 for i in range(100000))

# Profilowanie z zapisem do pliku
cProfile.run("wolna_funkcja()", "profil.stats")

# Analiza wyników
stats = pstats.Stats("profil.stats")
stats.sort_stats("cumulative")
stats.print_stats(10)   # Pokaż 10 najwolniejszych funkcji

Szybkie porównanie czasu wykonania:

from timeit import timeit

# Porównanie dwóch podejść
czas_lista = timeit("[i**2 for i in range(1000)]", number=10000)
czas_gen   = timeit("sum(i**2 for i in range(1000))", number=10000)
print(f"Lista: {czas_lista:.3f}s, Generator: {czas_gen:.3f}s")

Podsumowanie narzędzi debugowania

Narzędzie Zastosowanie Kiedy używać
print() Szybkie sprawdzenie wartości Proste skrypty, jednorazowe debugowanie
logging Trwałe śledzenie działania aplikacji Produkcja, złożone aplikacje
pdb / breakpoint() Interaktywne debugowanie krok po kroku Złożone błędy logiczne
traceback Pełny ślad stosu przy wyjątkach Debugowanie w obsłudze wyjątków
cProfile Profilowanie wydajności (czas w funkcjach) Optymalizacja wolnego kodu
timeit Precyzyjny pomiar czasu fragmentów kodu Porównywanie wydajności wariantów
memory_profiler Profilowanie zużycia pamięci Wycieki pamięci, duże dane
IDE debugger Graficzne debugowanie z breakpointami Codzienne programowanie
icecream (ic()) Czytelniejszy zamiennik print() do debugowania Szybkie debugowanie bez setupu

Skrócona ściąga komend pdb

Komenda Skrót Opis
next n Wykonaj następną linię (nie wchodząc w funkcje)
step s Wejdź do wnętrza wywoływanej funkcji
continue c Kontynuuj do następnego breakpointa
break N b N Ustaw breakpoint na linii N
print expr p Wyświetl wartość wyrażenia
pp expr pp Pretty-print wartości wyrażenia
list l Pokaż kod wokół bieżącej linii
where w Pokaż stos wywołań
up / down u/d Poruszanie się po stosie wywołań
quit q Zakończ debugger
!stmt Wykonaj dowolną instrukcję Pythona