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.
Ctrl+C na terminalu, „Interrupt kernel”10 Częstych błędów w pętli
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.
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.
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.
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.
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.
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ć.
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.
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).
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.
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:
| Iteracja | Stan | Zmienne PRZED ciałem | tego, co jest wydalane | Zmienne PO CIAŁA |
|---|---|---|---|---|
| 1 | … | … | … | … |
| 2 | … | … | … | … |
| … | … | … | … | … |
Off-by-one — osobna analiza
„Error per one”
Lista kontrolna przypadków granicznych
| Pytanie | Po 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 |
| Sprawa | Przykład | Ile iteracji |
|---|---|---|
| Zero | for n in []: | 0 — ciało nie zostanie stracone ani razu |
| Jeden | for n in [7]: | 1 |
| Dużo | for n in range(1000): | 1000 |
Zmienna cyklu po zakończeniu
for n in range(5):
pass
print(n) #4 to ostatnia wartość n którą przyjął
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:.
Podgląd: Zmień listę podczas wyszukiwania
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.