Last modified: June 06, 2026
This article is written in: 🇵🇱
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ą:
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):
if (count == 10).Krokowanie (stepping):
Inspekcja stanu:
Modyfikacja stanu:
Korzystanie z debuggera przynosi wiele korzyści, zarówno dla początkujących, jak i doświadczonych programistów:
print), debugger pozwala na bezpośredni podgląd i interakcję.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:
pdbpdb jest wbudowanym debuggerem Pythona, który, mimo swojej prostoty, oferuje ma wiele do zaoferowania:
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
pdbPodczas sesji z pdb masz dostęp do szeregu poleceń:
Nawigacja:
n / next: Przejście do następnej linii w bieżącej funkcji.s / step: Wejście do funkcji wywoływanej w bieżącej linii.r / return: Kontynuacja wykonania do momentu powrotu z bieżącej funkcji.c / continue: Kontynuacja wykonania do następnego breakpointa.Informacje o stanie:
l / list: Wyświetla kod źródłowy wokół bieżącej linii.w / where: Wyświetla stos wywołań (call stack).p / print: Wyświetla wartość wyrażenia.pp / pprint: Wyświetla wartość wyrażenia w sposób sformatowany.Zarządzanie breakpointami:
b / break: Ustawia breakpoint.cl / clear: Usuwa breakpoint.disable / enable: Dezaktywuje/aktywuje breakpoint.Pomoc i wyjście:
h / help: Wyświetla pomoc dla poleceń.q / quit: Kończy sesję debugowania.pdbpdb 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
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 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
pdb.set_trace() w kodzie, warto dodawać komentarze, aby nie pozostawić ich przypadkowo w wersji produkcyjnej.pdb, aby automatyzować powtarzające się czynności.pdb można integrować z narzędziami takimi jak pytest czy unittest, aby debugować testy jednostkowe.pdb, takie jak ipdb czy pudb, oferujące dodatkowe funkcje i interfejsy.Często najszybsze debugowanie odbywa się bez uruchamiania debuggera. Oto sprawdzone techniki:
print() i pprint()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
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:
assertjest wyłączane gdy Python jest uruchamiany z flagą-O(optymalizacja). Nie używaj do sprawdzania warunków krytycznych dla bezpieczeństwa — używajif ... raise.
traceback — szczegółowe ślady błędówimport 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)
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")
| 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 |
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 |