Debugowanie

W programach możemy znaleźć różnego rodzaju błędy. Dobrze jest umieć je rozróżniać po to, by móc szybciej namierzyć dany błąd.

  1. Błędy składni (syntax errors) zgłaszane są przez interpreter Pythona w czasie, gdy kod źródłowy tłumaczony jest na kod bajtowy. Z reguły mówią one o tym, że coś jest nie tak ze składnią programu. Przykładowo: pominięcie dwukropka na końcu wyrażenia def wyrzuci informację SyntaxError: invalid syntax (SyntaxError: nieprawidłowa składnia.).
  2. Błędy pracy programu (runtime errors) zgłaszane są przez interpreter, gdy coś niewłaściwego dzieje się z programem podczas jego pracy. W większości przypadków komunikat o błędzie zawiera informacje, w którym miejscu błąd wystąpił oraz które funkcje były wywoływane. Przykładowo: nieskończona rekurencja spowoduje podniesienie błędu spowodowanego osiągnięciem osiągnięciem maksymalnej możliwej ilości odwołań.
  3. Błędy semantyki (semantic errors) występują, gdy program kompiluje się dobrze, pracuje bez zarzutu, ale nie zwraca oczekiwanych rezultatów. Na przykład: działania w jakimś wyrażeniu nie będą wykonane w takiej kolejności, w jakiej zakłada programista, powodując zwrócenie nieoczekiwanego wyniku.

Rozpoczynając debugowanie w pierwszej kolejności należy rozstrzygnąć z jakim błędem mamy do czynienia. Pomimo tego, że w następnej części opisujemy błędy posegregowane według typu błędu, niektóre techniki można wykorzystywać też w innych sytuacjach.

Błędy składni

gdy już zidentyfikujemy błędy składni (syntax errors) są one z reguły łatwe do naprawienia. Niestety informacje o błędzie, jakie dostajemy nie są zazwyczaj zbyt pomocne. Najczęstszymi informacjami będą SyntaxError: invalid syntax, co tłumaczy się jak BłądSkładni: nieprawidłowa składnia czy SyntaxError: invalid token (BłądSkładni: nieprawidłowy znak), które jak widać nie wnoszą wiele.

Z drugiej strony, komunikat zawiera informację, gdzie w programie wystąpił błąd. Właściwie to poinformuje Cię, gdzie Python zauważył problem, co nie zawsze oznacza, że błąd będzie zlokalizowany w tym samym miejscu. Czasami błąd występuje w linii, poprzedzającej miejsce wystąpienia informacji o błędzie.

Jeżeli budujesz program stopniowo, to powinieneś dobrze wiedzieć, gdzie wystąpił błąd. Będzie on w ostatniej napisanej przez Ciebie linijce.

Jeżeli kopiujesz kod z książki to zacznij od bardzo starannego porównania swojego kodu z kodem, znajdującym się w książce. Sprawdź każdy znak. Pamiętaj, że książka też może zawierać błędy, więc jeżeli widzisz coś co przypomina błąd składni, to jest to możliwe.

Oto kilka rad jak unikać najbardziej pospolitych błędów składni:

  1. Upewnij się, że nie używasz żadnych słów kluczowych jako nazw zmiennych.
  2. Upewnij się, że na końcu każdego wyrażenia, rozpoczynającego blok instrukcji (for, while, if i def) widnieje dwukropek.
  3. Sprawdź, czy wcięcia są konsekwentne. Można wcinać kod, używając zarówno spacji, jak i tabulacji, ale najlepiej ich nie mieszać. Każdy kolejny poziom powinien być wcięty jednakowo.
  4. Upewnij się, że wszystkie ciągi znaków posiadają, odpowiadające sobie cudzysłowy.
  5. Jeżeli używasz wielolinijkowych ciągów znaków, zaczynających się od potrójnych cudzysłowów (pojedynczych bądź podwójnych) upewnij się, że kończą się one poprawnie. Niepoprawnie zakończony ciąg tego typu może spowodować podniesienie błędu invalid token (nieprawidłowy znak) na samym końcu programu. Może też traktować następującą po cudzysłowach część programu jak ciąg znaków, który kończyć się będzie na początku kolejnego ciągu znaków. W tym wypadku możemy wcale nie dostać komunikatu o błędzie!
  6. Niedomknięty nawias – (, {, or [ – spowoduje przejście interpretera do kolejnej linii kodu, która będzie traktowana jak kontynuacja obecnej linii. Zazwyczaj błąd wystąpi praktycznie od razu w następującej linii.
  7. Sprawdź klasyczny błąd użycia = zamiast == przy porównaniach w instrukcjach warunkowych.

Jak nic nie działa, przejdź do kolejnego rozdziału…

Nie mogę uruchomić mojego programu niezależnie od tego jak bardzo się staram.

Jeżeli kompilator zgłasza błąd, a Ty go nie widzisz, może przyczyna tkwi w tym, że zarówno Ty, jak i kompilator widzicie inny program. Sprawdź swoje środowisko programistyczne, aby upewnić się, że program, który właśnie edytujesz to ten sam, który Python stara się uruchomić. Jeżeli nie jesteś pewien to postaraj się umieścić rozmyślnie jakiś oczywisty błąd na samym początku kodu. Teraz ponownie uruchom go lub zaimportuj. Jeżeli interpreter nie widzi właśnie dopisanego błędu to coś jest nie tak z konfiguracją środowiska programistycznego, w którym pracujesz.

Jeżeli zdarzy Ci się podobny przypadek, możesz na przykład rozpocząć od kompletnie nowego programu, jak „Hello, World!” i upewnić się, że uda Ci się uruchomić taki przewidywalny program. Następnie dodawaj stopniowo kolejne części kodu poprzedniego programu do nowego, działającego.

Błędy pracy programu (runtime errors)

W momencie, gdy nasz program jest wolny od błędów składniowych Python może go zaimportować lub przynajmniej go uruchomić. Cóż złego może się stać?

Mój program nic nie robi.

Tego typu sytuacja zdarza się często, gdy Twój plik zawiera funkcje i klasy, ale w rzeczywistości nigdzie ich nie woła. Tego typy zachowanie może być oczywiście z góry założone przez programistę. Z taką sytuacją mamy do czynienia wtedy, gdy plik ten ma z definicji być modułem, zawierającym klasy i funkcje, które mają być importowane w innych programach.

Jeżeli nie masz takich zamiarów, upewnij się, że wołasz jakąś funkcję, aby rozpocząć wykonywanie programu lub wywołaj jakąś funkcję z interaktywnej powłoki. Spójrz też w poniższy rozdział Przepływ instrukcji.

Mój program się wiesza.

Jeżeli program się zatrzymuje i wygląda na to, że nic szczególnego nie robi to mówimy wtedy, że program się zawiesił. Najczęściej oznacza to, że wykonuje właśnie nieskończoną pętlę lub też w nieskończoność odwołuje się rekurencyjnie.

  1. Jeżeli podejrzewasz jakąś konkretną pętlę dodaj do linii, poprzedzającej blok pętli wyrażenie print, drukujące informację o wejściu w pętlę oraz podobne zaraz po zakończeniu tego bloku, oznajmiające właśnie o skończeniu pętli.
  2. Uruchom program. Jeżeli widzisz pierwszą wiadomość, a nie widzisz drugiej to właśnie namierzyłeś nieskończoną pętlę. Przejdź do rozdziału Pętle nieskończone.
  3. Najczęściej nieskończone wywołanie rekurencyjne pozwoli na pracę programu przez jakąś chwilę, a następnie wyrzuci błąd RuntimeError: Maximum recursion depth exceeded. Jeżeli dostaniesz taki komunikat, przejdź do poniższego rozdziału Nieskończone wywołania rekurencyjne.
  4. Jeżeli nie widzisz podobnej informacji o błędzie, ale podejrzewasz, że kłopot sprawia funkcja zaprogramowana rekurencyjnie to również możesz użyć technik opisanych w rozdziale Nieskończone wywołania rekurencyjne.
  5. Jeżeli podjęte kroki nie pomogą, zacznij testować pozostałe pętle oraz funkcje i metody zaprogramowane rekurencyjnie.
  6. Jeżeli powyższe punkty nie zadziałają to najprawdopodobniej nie rozumiesz przepływu wykonywania instrukcji w swoim programie. Przejdź do rozdziału Przepływ instrukcji.

Pętle nieskończone

Jeżeli uważasz, że w programie któraś pętla jest nieskończona i dodatkowo podejrzewasz konkretną pętlę warunkową, dodaj na końcu pętli wyrażenie print tak, aby drukowało wartości i zmienne podlegające warunkowi oraz wynik samego warunku.

Na przykład:

while x > 0 and y < 0:
    # zrob cos ze zmienna x
    # jak i ze zmienna y

    print("x: ", x)
    print("y: ", y)
    print("warunek: ", (x > 0 and y < 0))

Uruchamiając program, zobaczycie trzy linie dla każdorazowego obrotu pętli. Ostatni obrót pętli powinien zakończyć się warunkiem False. Jeżeli pętla działa przez cały czas, będziecie w stanie zobaczyć, jak zmieniają się wartości trzymane w zmiennych x i y i może będziecie w stanie wymyślić, co powoduje, że wartości te nie zmieniają się tak jak powinny.

W środowisku programistycznym takim jak PyScripter można ustawić przerwanie na samym początku pętli i przejść pętlę krok po kroku. Kiedy tak właśnie będziecie szukać błędów, zwróćcie uwagę na wartości trzymane przez x i y poprzez najechanie na nie kursorem.

Programowanie i debugowanie wymaga oczywiście wiedzy na temat tego, co w zasadzie dany algorytm winien robić. Jeżeli nie rozumiesz, co dzieje się z x i y to badanie ich wartości nie będzie zbytnio pomocne. Wtedy prawdopodobnie najlepszą metodą debugowania kodu jest odejść od komputera i przede wszystkim zrozumieć jak program/algorytm działa.

Nieskończone wywołania rekurencyjne

Zazwyczaj nieskończone wywołanie rekurencyjne spowoduje, że program będzie działał jakiś czas, by w końcu podnieść wyjątek Maximum recursion depth exceeded.

Jeżeli podejrzewasz, że jakaś funkcja lub metoda powoduje nieskończoną ilość wywołań rekurencyjnych sprawdzanie ich rozpocznij od upewnienia się, że istnieje przypadek podstawowy. Inaczej mówiąc, powinien istnieć jakiś warunek, który spowoduje że funkcja/metoda zwróci jakąś wartość bez odwołania rekursywnego. Jeżeli takowego nie ma to powinieneś przemyśleć swój algorytm tak, by zidentyfikować przypadek podstawowy.

Jeżeli przypadek podstawowy istnieje, ale rekurencja nie wydaje się do niego zbiegać, dodaj wyrażenie print na początku funkcji lub metody tak, aby drukowało wartości parametrów. Teraz za każdym razem, gdy uruchomisz program powinieneś widzieć wartości parametrów za każdym razem, gdy program odwoła się do danej funkcji. Jeżeli wartości nie zmierzają w kierunku przypadku podstawowego powinieneś otrzymać jakieś wskazówki dlaczego tak się nie dzieje.

Raz jeszcze, jeżeli pracujesz w środowisku, wspomagającym ustawianie przerwań, proste wykonywanie programu krok po kroku i możliwość sprawdzania zmiennych naucz się dobrze używać tego narzędzia. Zgodnie uważamy, że właśnie takie uruchamianie programu, krok po kroku umożliwia najlepsze teoretycznie zrozumienie tego, jak działają obliczenia. Używajcie narzędzi, jeżeli je posiadacie!

Przepływ instrukcji

Jeżeli nie jesteście pewni, w jaki sposób wykonywane są kolejne instrukcje w Twoim programie zawsze na początku każdej funkcji dodawaj wyrażenie print, drukujące wiadomość o tym, że dana funkcja foo jest właśnie wołana. Owo foo to oczywiście winna być nazwa wołanej funkcji.

Teraz uruchomienie programu umożliwi prześledzenie wywoływania każdej funkcji.

Jeżeli dalej nie jesteś pewien, prześledź program, używając debugera.

Uruchomiany program podnosi wyjątek.

Jeżeli coś pójdzie nie tak jak powinno, Python drukuje komunikat, w którym zawarta jest nazwa wyjątku, linia programu, w którym wystąpił wyjątek oraz pełny komunikat o błędzie (traceback).

Ustaw przerwanie na lini, która podnosi wyjątek i porozglądaj się uważnie po kodzie.

Komunikat o błędzie zidentyfikuje funkcję, która właśnie pracuje oraz funkcję, która wywołała działającą funkcję oraz funkcję, wywołującą tę właśnie funkcję i tak dalej… Innymi słowy, prześledzi kolejne wywołania funkcji, które doprowadziły Cię do miejsca, w którym właśnie jesteś. Do komunikatu dodana będzie też informacja o numerach linii w Twoim pliku, gdzie każde z tych wywołań miało miejsce.

Pierwszym krokiem byłoby sprawdzenie miejsca w programie, gdzie ów błąd wystąpił i oszacowanie, czy jesteśmy w stanie coś poprawić. Oto i kilka najczęstszych błędów pracy programu:

NameError
[błąd nazwy zmiennej] Odwołujesz się do zmiennej, która nie istnieje w danym zasięgu. Zapamiętaj, że lokalne zmienne są lokalne. Nie można się do nich odwołać spoza funkcji, w której były zdefiniowane.
TypeError

[błąd typu zmiennej] Tutaj mamy kilka możliwości:

  1. Chcesz niewłaściwie użyć danej wartości. Np: odwołujesz się do elementu ciągu znaków listy czy krótki za pomocą indeksu, który nie jest typu int.
  2. Istnieje niezgodność pomiędzy formatem zmiennych w ciągu znaków, a zmiennymi poddanymi owej konwersji. Może się tak stać, jeżeli liczba zmiennych jest nieodpowiednia lub odwołujemy się do złego typu konwersji.
  3. Przekazujesz niewłaściwą ilość argumentów do funkcji/metody. W przypadku metod sprawdź definicję metody, w szczególności upewnij się, czy zawiera ona parametr self jako pierwszy. Potem spójrz na wywołanie danej metody. Upewnij się, że wywołujesz metodę na obiekcie właściwego typu i podajesz poprawnie wszystkie argumenty.
KeyError
[niewłaściwy atrybut] Odwołujesz się do elementu słownika, używając klucza nie występującego w słowniku.
AttributeError
[niewłaściwy atrybut] Starasz się odwołać do atrybutu bądź metody, który nie istnieje.
IndexError
[niewłaściwy indeks] Indeks, którym starasz się odwołać do elementu listy, krótki lub ciągu znaków jest większy od ich długości mniej jeden. Najlepiej od razu przed odwołaniem się do zmiennej dodaj wyrażenie print, by zobaczyć wartość owego indeksu oraz długość wyrażenia iterowalnego len(obiekt). Czy długość obiektu jest właściwa? Czy indeks jest poprawny?

Mam już tyle wywołań print, że nie widzę nic innego.

Jednym z największych kłopotów z używaniem polecenia print do debugowania jest to, że można zostać wręcz pogrzebanym pod lawiną informacji. Istnieją dwa sposoby, aby sobie z tym poradzić: skrócić czy może raczej uprościć informacje zwracane za pomocą print lub uprościć program.

Aby skrócić informację z debugowania za pomocą print, możemy usunąć lub zakomentować wszystkie komendy print, które nic nie wnoszą, połączyć je, albo tak sformatować treść, by można go było łatwiej zrozumieć.

Jest kilka możliwości uproszczenia programu. Po pierwsze uprość problem, który rozwiązuje Twój program. Np: jeżeli sortujesz jakąś tablicę, posortuj najpierw małą tablicę. Jeżeli program oczekuje wprowadzenia danych od użytkownika, podaj najprostszy możliwy zestaw danych, który już powoduje pojawienie się problemu.

Druga możliwość - posprzątaj kod. Usuń wszystkie nieistotne części, przeorganizuj program tak, by był tak prosty do czytania jak to możliwe. Np: jeżeli podejrzewasz, że problem leży w najbardziej zagnieżdżonej części programu postaraj się przepisać tę część kodu w prostszy sposób. Jeżeli oczekujesz, że winna jest jakaś sporej wielkości funkcja (o dużej ilości linii kodu) - podziel ją na mniejsze funkcje i przetestuj je osobno.

Bardzo często sam proces opracowywania najprostszego testu doprowadza Cię do znalezienia błędu. Jeżeli okaże się, że program działa w jednej sytuacji, a w innej nie działa, możliwe, że da Ci to pojęcie dlaczego tak się dzieje.

Podobnie jest z przepisywaniem poszczególnych części programu. Pomaga to często w znalezieniu subtelnych błędów. Jeżeli zmieniasz coś, co nie powinno mieć wpływu na działanie programu, a ma, powinno dać ci to do myślenia.

Możesz też opakować swoje informacje z debugowania jakimś warunkiem, tak, by je w większości ukryć. Np: jeżeli starasz się znaleźć jakiś element, używając przeszukiwania binarnego, a coś nie działa, możesz ograniczyć print, służący do debugowania do warunku: jeżeli ilość kandydatów na szukany element jest mniejsza niż (przykładowo) 6 to wydrukuj informację, jeśli jest większa to nie drukuj.

Podobnie możesz ustawić przerwania tylko dla spełnionego konkretnego warunku. Ustaw więc przerwanie dla danego wyrażenia, a później przeedytuj przerwanie tak by wyrażało m/w „przerwij w tym miejscu tylko wtedy, gdy dane wyrażenie będzie prawdziwe”.

Błędy semantyki

W pewnym sensie błędy semantyki są najtrudniejszymi błędami do zdebugowania, ponieważ zarówno kompilator, jak i system nie dostarczą nam informacji, gdzie leży problem. Tylko Ty wiesz co Twój program powinien robić i tylko Ty możesz stwierdzić, że tego nie robi.

Pierwszy krok to stworzenie połączenie kodu z zachowaniem, które obserwujesz. Potrzebna będzie hipoteza, opisująca co program tak naprawdę robi. Jednym z istotnych zagadnień jest to, że komputery są obecnie naprawdę szybkie.

Często będziesz sobie życzył tego, żeby program zwolnił na tyle, by człowiek mógł prześledzić jego działanie. Możesz wykorzystać do tego debuger. Okazuje się jednak, że czas potrzebny na umieszczenie w kodzie tych kilku poleceń print zajmuje o wiele mniej czasu od poprawnego ustawienia debugera, powstawiania i usuwania przerwań oraz uruchamiania programu krok po kroku, instrukcja po instrukcji by namierzyć błąd.

Mój program nie działa.

Powinieneś zadać sobie następujące pytania:

  1. Czy istnieje coś, co program powinien wykonywać, ale tego nie robi? Znajdź tę część programu, która wykonuje ową funkcję (cześć kodu) i upewnij się, że w rzeczywistości wywołujesz to, co myślisz, że wywołujesz.
  2. Czy dzieje się coś co nie powinno mieć miejsca? Ponownie - znajdź ten kawałek kodu (funkcję), który jest za to odpowiedzialny i sprawdź, czy jest on wołany, gdy nie powinien.
  3. Czy jakaś część kodu wywołuje efekt, którego się nie spodziewasz? Upewnij się, że rozumiesz analizowaną część kodu, w szczególności, jeżeli dotyczy wywoływania funkcji/metod, pochodzących z innych modułów Pythona. Przeczytaj dokumentację funkcji, których używasz. Wypróbuj ich działanie, pisząc proste testy i sprawdzając ich rezultaty.

By nauczyć się programować, potrzebujesz stworzyć swoje własne wyobrażenie o tym, jak programy działają. Jeżeli napiszesz już program, ale nie realizuje on tego, co sobie założyłeś to bardzo często błędy nie są w samym kodzie, ale w Twoim jego wyobrażeniu.

Najlepszą metodą, aby naprawić owo wyobrażenie to podzielić program na poszczególne komponenty (zazwyczaj funkcje i metody), a następnie przetestować je niezależnie. Gdy już znajdziesz rozbieżność pomiędzy swoim wyobrażeniem, a rzeczywistością, możesz zacząć rozwiązywać problemy.

Prawda jest taka, że powinieneś zarówno budować, jak i testować komponenty programu wtedy, gdy je konstruujesz. Jeżeli napotkasz jakąś nieścisłość, to będzie to tylko niewielka ilość kodu, która jest niepoprawna.

Mam tu takie potężne włochate wyrażenie, które nie działa jak powinno.

Używanie skomplikowanych wyrażeń jest w porządku tak długo, jak długo są one czytelne. Mogą być one jednak trudne do zdebugowania. W rzeczywistości lepiej jest rozbić skomplikowane wyrażenie, wykorzystując przypisania do tymczasowych zmiennych.

Przykładowo:

self.hands[i].addCard(self.hands[self.findNeighbor(i)].popCard())

Możemy przepisać tak:

neighbor = self.findNeighbor(i)
pickedCard = self.hands[neighbor].popCard()
self.hands[i].addCard(pickedCard)

Jawna wersja jest łatwiejsza do czytania, ponieważ nazwy zmiennych zapewniają dodatkową dokumentację, jest też łatwiejsza do debugowania, ponieważ można sprawdzić typy poszczególnych zmiennych tymczasowych oraz podejrzeć i sprawdzić wartości przez nie trzymane.

Innym problem z dłuższymi wyrażeniami może pojawić się, gdy kolejność operacji nie odpowiada tej, jakiej się spodziewamy. Przykładowo, chcąc zaprogramować wyrażenie x/2pi w Pythonie, możesz napisać:

y = x / 2 * math.pi;

Nie jest to do końca poprawne, ponieważ mnożenie i dzielenie mają identyczne pierwszeństwo i są rozwijane od lewej do prawej. Tak więc wyrażenie to da wynik: (x/2)pi.

Dobrą metodą uniknięcia błędów w tego typu wyrażeniach jest dodanie nawiasów w taki sposób, by kolejność wykonywania operacji była dana jawnie:

y = x / (2 * math.pi);

Zawsze, gdy nie jesteś pewien kolejności operacji używaj nawiasów. Nie tylko program na pewno będzie poprawny (w tym sensie, że obliczy to, co chciałbyś by obliczył), ale będzie też dużo bardziej czytelny dla wszystkich tych, którzy może nie pamiętają reguł następstwa operacji.

Mam tu taką funkcję/metodę, która nie zwraca tego czego oczekuję.

Jeżeli przy wyrażeniu return stoi skomplikowane wyrażenie, to nie masz szans wydrukować co ów return zwraca. Możesz znowu użyć tymczasowej zmiennej. Na przykład zamiast pisać:

return self.hands[i].removeMatches()

można by dać:

count = self.hands[i].removeMatches()
return count

Teraz istnie możliwość wyświetlenia i zbadania wartości trzymanej przez count jeszcze przez zwróceniem jej.

Na serio, utknąłem i potrzebuję pomocy.

Przede wszystkim, postaraj się odejść od komputera na kilka minut. Komputery emitują fale, które wpływają na mózg i mogą wywołać:

  1. Frustrację i/lub gniew.
  2. Przesądy (komputery mnie nienawidzą) oraz myślenie magiczne (ten program działa tylko jeżeli noszę mój kapelusz odwrotnie).
  3. Programowanie w stylu błądzenia losowego (próba napisania każdego możliwego programu, a później wybrania tego, który robi to co powinien).

Jeżeli uważacie, że dotknął Was któryś w powyższych problemów, wstańcie od komputera i idźcie na spacer. Gdy już ochłoniecie, pomyślcie o programie. Cóż takiego on robi? Jakie są możliwe przyczyny takiego działania? Kiedy ostatnio program działał i co takiego zrobiliście później?

Czasem wystarczy dać sobie trochę czasu, by znaleźć błąd. Często znajdujemy błędy, gdy nie siedzimy przy komputerze i pozwalamy naszym myślom błądzić i wędrować. Jedne z najlepszych miejsc w których znajduje się błędy w kodzie to pociągi, prysznice i nasze własne łóżka, na chwilę przed zaśnięciem.

Nie no, na serio potrzebuję pomocy.

Zdarza się. Nawet najlepsi programiści zatną się od czasu do czasu. I to nie przy goleniu. Czasami pracujesz nad danym programem już tak długo, że nie masz szans zauważyć błędu. Potrzebna jest wtedy świeża para oczu.

Zanim poprosisz kogoś o pomoc, upewnij się, że wykorzystałeś już wszystkie techniki, o których pisaliśmy. Twój program powinien być tak prosty jak to możliwe, a Ty sam powinieneś pracować na najmniejszych możliwych danych wejściowych, które wywołują błąd. Program powinien już posiadać wyrażenia print w strategicznych miejscach (a treść przez nie produkowana winna być ścisła i jasna). Powinieneś też zrozumieć, gdzie tkwi problem do tego stopnia, by opisać go ściśle.

Jeżeli już prosisz o pomoc osobę trzecią, upewnij się, że dostarczasz jej wszystkie informacje, jakich potrzebują:

  1. Jeżeli wyskakuje komunikat o błędzie, to co to za komunikat i na którą część programu wskazuje.
  2. Jaka była ostatnia rzecz, którą zrobiłeś przed tym jak wystąpił błąd? Które linie kodu dodałeś jako ostatnie lub jak wygląda test który wywołuje błąd?
  3. Czego już próbowałeś i jaką lekcję z tego wyciągnąłeś?

Dobrzy nauczyciele i osoby, starające się Ci pomóc również zrobią coś, co nie powinno Cię urażać: nie do końca uwierzą w Twoje zapewnienia, gdy oznajmisz „jestem przekonany, że wszystkie procedury działają poprawnie, używam też poprawnych danych!”. Będą chcieli potwierdzić i sprawdzić to i owo samodzielnie. Ostatecznie to Twój program ma błędy. Twoje zrozumienie oraz analiza programu nie wyeliminowały ich do tej pory. Musisz się więc spodziewać, że ktoś zakwestionuje Twoje słowa. Zdobywając umiejętności, pomagając innym, będziesz kiedyś robił to samo dla nich.

Jeżeli już znajdziesz błąd, zastanów się chwilę, co mógłbyś zrobić by namierzyć go szybciej. Następnym razem, gdy zobaczysz podobne zachowanie, będziesz w stanie zlokalizować błąd dużo szybciej.

Pamiętaj, celem nie jest zmuszenie programu do działania. Cel to nauka jak budować programy, które działają.

Następna część - Python Beyond the Browser