Rozdział 10 · Trochę automatyzacji!

Debugowanie pętli

Dziesięć typowych błędów pętli i uniwersalna metoda ich znajdowania — tabela śledzenia stworzona ręcznie, łańcuch znaków linia po linii.

Jak czytać czyjś (i własny) zepsuty cykl

Pętle — najczęstsze miejsce, w którym program dzieła, ale daje błędny wynik, albo całkowicie się zawiesza. Dobrą wiadomością jest to, że zdecydowana większość takich błędów pasuje do nich Łącznie istnieje tuzin typowych scenariuszy. Przyjrzyjmy się im po kolei.

Ważne: NIE będziemy uruchamiać zawieszonego kodu
Niektóre z poniższych przykładów opisują nieskończone pętle — w praktyce tej strony będziesz czytać i przewidywać zachowanie kodu, zamiast uruchamiać prawdziwą pętlę zawieszającą się w przeglądarce. Jeśli kod w twoim programie jest naprawdę zamrożony, zatrzymaj go ręcznie (Ctrl+C na terminalu, „Interrupt kernel”

10 Częstych błędów w pętli

1. Zapomniałem zaktualizować status
bug.py
while count < 5:
    print(count)
    # zapomniałem count += 1

Warunek nigdy nie stanie się fałszywy — nieskończona pętla. Sprawdź, czy w while znajduje się łańcuch znaków, która zmienia zmienną względem warunku.

2. Błąd na jeden na range()
bug.py
for i in range(1, 10):
    print(i)  # nie wyświetli 10!

range(1, 10) daje 1..9 — jeśli trzeba dodać 10, to range(1, 11). Klasyczny błąd off-by-one.

3. Nieprawidłowe wgniecenie korpusu
bug.py
for n in chisla:
print(n)  # SyntaxError: wcięcia oczekiwano

Ciało pętli musi być wcięte. Bez tego Python nawet nie uruchomi programu.

4. Licznik jest zerowany wewnątrz pętli
bug.py
for n in chisla:
    kolichestvo = 0
    kolichestvo += 1

kolichestvo jest resetowany przy każdej iteracji — na końcu zawsze ma 1, a nie całkowitą liczbę. Inicjalizacja powinna nastąpić PRZED pętlą, a nie wewnątrz.

5. break na niewłaściwym poziomie wcięcia
bug.py
for n in chisla:
    if n == 5:
    break  # nie w ciele if!

break, stojący poza if (z powodu wcięcia), zadziała przy pierwszej iteracji niezależnie od warunku. Sprawdzaj wcięcia tak samo uważnie, jak warunki.

6. continue przed aktualizacją
bug.py
while count < 5:
    if count == 2:
        continue
    print(count)
    count += 1

Gdy count == 2, continue pomija count += 1 — i count na zawsze pozostanie równe 2. Nieskończona pętla, i jest szczególnie trudno ją zauważyć.

7. Pomyłka zmiennych zagnieżdżonych pętli
bug.py
for i in range(3):
    for i in range(4):
        print(i)

Wewnętrzna pętla używa tej samej nazwy i, co zewnętrzna — zewnętrzna wartość i zostaje nadpisana i utracona. Użyj różnych nazw, na przykład i i j.

8. Nieprawidłowy krok negatywny
bug.py
for n in range(10, 0):
    print(n)  # nic nie wywoła

range(10, 0) z domyślnym krokiem +1 nigdy nie przejdzie od 10 do 0 — potrzebny jest range(10, 0, -1).

9. Niezgodność stanu i warunku
bug.py
while spisok:
    print(spisok[0])
    # zapomniałem usunąć przedmiot z spisok

Warunek sprawdza spisok (o ile lista nie jest pusty), ale ciało nigdy nie zmniejsza lista — nieskończona pętla, choć wygląda inaczej niż licznik.

10. Warunek fałszywy od samego początku
bug.py
n = 10
while n < 5:
    print(n)
    n += 1

Ciało nie wykona się ani razu — zero iteracji. To nie jest błąd sam w sobie, ale częsta niespodzianka, jeśli wcześniej nie sprawdzono warunku.

Metoda debugowania: Ręczna tabela śledzenia

Kiedy pętla działa nie tak, jak się tego spodziewano, najpewniejszy sposób, żeby to sprawdzić, to zbudować tę samą tabelę śledzenia, której używaliśmy przez cały ten rozdział, ale teraz dla prawdziwego, zepsutego kodu:

IteracjaStanZmienne PRZED ciałemtego, co jest wydalaneZmienne PO CIAŁA
1
2
Wypełniaj tabelę linijka po linijce, jakbyś był Python
Nie próbuj zgadywać całego wyniku. Weź pierwszą iterację, zapisz warunek uczciwie, wartości zmiennych przed i po ciele, i powtarzaj, aż zobaczysz, gdzie oczekiwanie odbiega od tego, co faktycznie się dzieje.

Off-by-one — osobna analiza

„Error per one”

12345678910stop — wyłączone
range(1, 10) to ostatnia wartość 9, a nie 10! Projekt
1234567891011stop — wyłączone
range(1, 11) — poprawne, jeśli chcesz osiągnąć 10 włącznie

Lista kontrolna przypadków granicznych

PytaniePo co sprawdzać
Co się stanie, jeśli nie ma żadnych powtórek (pusta sekwencja)?ciało pętli w ogóle się nie wykonuje, niezależnie od tego, czy kod się zepsuje po pętli
Co się stanie przy jednym powtórzeniu?najczęstszym źródłem błędów na jednostkę
Czy ostatnia granica jest uwzględniona tam, gdzie jest potrzebna?stop nie jest domyślnie włączony dla range() i slicerów
Czy kierunek kroku pokrywa się z kierunkiem start→stop?pozytywny krok dla wzrostu, negatywny dla spadku
SprawaPrzykładIle iteracji
Zerofor n in []:0 — ciało nie zostanie stracone ani razu
Jedenfor n in [7]:1
Dużofor n in range(1000):1000

Zmienna cyklu po zakończeniu

peremennaya_posle_cikla.py
for n in range(5):
    pass

print(n)  #4 to ostatnia wartość n którą przyjął
Zmienna nie znika po for
Po ukończeniu for zmienna pętli pozostaje powiązana z ostatnią uzyskaną wartością — w przeciwieństwie do niektórych innych języków, w Python pętle nie mają osobnego „zakresu widoczności”. Wyjątek: jeśli sekwencja była pusta, ciało nie wykonało się ani razu, a zmienna pętli w ogóle nie zostało utworzone — odwołanie się do niej po pętli w tym przypadku spowoduje NameError.

Nazwy zmiennych pętli

Krótkie nazwy takie jak i, j, k — dawna tradycja dla prostych liczników (szczególnie w zagnieżdżonych pętlach), ale gdy tylko zmienna pętli ma realny sens — nazwij ją sensownie: for slovo in slova: jest łatwiejszy do czytania niż for x in y:.

Zmienne cieniowanie (shadowing)
Jeśli ręcznie przypiszesz nową wartość zmiennej pętli wewnątrz pętli lub użyjesz nazwy, która już istniała poza pętlą, możesz przypadkowo usunąć pożądaną wartość. Szczególnie łatwo jest popełnić ten błąd w zagnieżdżonych pętlach o tej samej nazwie (patrz błąd nr 7 powyżej).

Podgląd: Zmień listę podczas wyszukiwania

Nie zmieniaj lista, którą teraz próbujesz
Usuwanie lub dodawanie elementów w lista wprost podczas cyklu for элемент in spisok: może prowadzić do tego, że niektóre elementy zostaną pominięte lub przetworzone dwukrotnie. Dokładnie omówimy ten temat i bezpieczne sposoby jego obejścia w rozdziale o listach — na razie wystarczy wiedzieć, że tak nie należy robić.

Trochę o występach

Każde dodatkowe zagnieżdżenie pętli mnoży liczbę iteracji (widzieliśmy to już na przykładzie tabliczki mnożenia). Na razie musisz policzyć tysiące, a nie miliony wartości – różnica jest niewidoczna. Formalne wyjaśnienie szybkości algorytmów czeka na Ciebie już teraz Intuicja wystarczy: zagnieżdżona pętla w pętli zwykle działa zauważalnie wolniej niż ta cykl o tej samej wielkości.

Praktyka: Znajdź błęda w pętli
interaktywny laptop bezpośrednio w przeglądarce – Python 3.14 przez Pyodide, bez instalacji
Otwórz praktykę →
Praktyka: off-by-one — liczyć iteracje
interaktywny laptop bezpośrednio w przeglądarce – Python 3.14 przez Pyodide, bez instalacji
Otwórz praktykę →