Loeng 9: Erisused, erisuste haldamine, erisuste hierarhia, konstruktsioon: try/except/else/finally, lausend raise, silumine, süntaksiviga, täitmisaegne viga, loogikaviga, versioonikontroll¶

Viimati uuendatud 28.10.2024.

Koodinäited¶

Pythoni erisused ja süntaksivead¶

Kui sa oled proovinud iseseisvalt selle kursuse ülesandeid lahendada siis oled kindlasti veateadetega tuttav. Lihtsustatult öeldes saame Pythonis eristada kahte tüüpi vigu: süntaksivigu (syntax errors) ja erisusi (exceptions). Mõlemad vead on täpsemalt defineeritud allpool. Mõlemat tüüpi vead peatavad programmi töö ja Pythoni interpretaator püüab kasutajat ollukorrast võimalikult täpselt informeerida.

Süntaksiviga (Syntax error)¶

sünkatsiviga tekib kui programmeeria eksib koodisüntaksi reeglite vastu. Kasutaja unustab kooloni, kirjutab lausendi valesti, eksib taande vastu jne.

In [1]:
while True
    print('Hello world')
  Cell In[1], line 1
    while True
              ^
SyntaxError: expected ':'
In [2]:
print max([1, 2])
  Cell In[2], line 1
    print max([1, 2])
    ^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?

Erisused (Exceptions)¶

Erisus või erind tõstatakse kui kood on süntaktiliselt õige. Kasutaja pole eksinud süntaksireeglite vastu, kuid sellegipoolest programmi töö üldjuhul peatatakse.

Programmid töötlevad andmeid (andmetüüpe) kui sisendandmed ei vasta programmi eeldustele tõstatakse erisus. Kui pole teisiti öeldud siis erisuse tõstmisel programmi interpreteerimine peatatakse.

Erisue tõstatamisel genereeritakse nn. traceback mis sisaldab muuseas infot erisuse asukoha (rea numbri) kohta (ei pruugi õige olla). Lisaks näidatakse erisuse asukohta koodist vasakul pool oleva pika noole (---->) ja/või alumisel real oleva caret'i (^) abil. Tracebacki loeme alt ülesse (selline lugemine on eriti kasulik kui traceback on pikk). Viimane rida sisaldab tõstatatud erisuse nime ja kirjeldust.

Näiteid: Pythonit õppides on paljusid erisusi ja nende tagamaid on võimalik lennu pealt mõista.

In [3]:
def jaga(a, b):
    return a / b

jaga(1, 2)  # --> Erisus puudub, kõik on korras.
Out[3]:
0.5
In [4]:
jaga(1, 0)  # --> ZeroDivisionError <-- Erisuse nimi.
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[4], line 1
----> 1 jaga(1, 0)

Cell In[3], line 2, in jaga(a, b)
      1 def jaga(a, b):
----> 2     return a / b

ZeroDivisionError: division by zero
In [5]:
jaga(1, '0')  # --> Erisus: TypeError.
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[5], line 1
----> 1 jaga(1, '0')

Cell In[3], line 2, in jaga(a, b)
      1 def jaga(a, b):
----> 2     return a / b

TypeError: unsupported operand type(s) for /: 'int' and 'str'
In [6]:
import math
math.sqrt(-1)  # --> Erisus: ValueError.
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[6], line 2
      1 import math
----> 2 math.sqrt(-1)

ValueError: math domain error

NB! Kuna Python on väga-kõrgetasemeline skriptimiskeel siis võivad pealtnäha sarnased vead tõstatada erinevaid erisuse kommentaare. Olukord paraned iga uuema Pythoni interpretaatori versiooniga. Algajale potensiaalselt segadust tekitav.

Näide olukorrast kus erisus on peidetud keerukama protsessi sisse. Väljundis on näha kogu protsessi, mille teostamisel tekkis viga:

In [7]:
def kolmas_funk(a):
    print(int(a))  # NB! Püüab teisendada sõnet: "Väljatrüki nimi on 'Traceback'.".

def teine_funk(a, b):
    c = a + b  # Sõnede konkatenatsioon.
    print(c)   # --> Väljatrüki nimi on 'Traceback'.
    kolmas_funk(c)  # Funktsiooni kolmas_funk väljakutse.

def esimene_funk():
    teine_funk("Väljatrüki nimi on ", "'Traceback'.")  # Funktsiooni teine_funk väljakutse.

esimene_funk()
Väljatrüki nimi on 'Traceback'.
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[7], line 12
      9 def esimene_funk():
     10     teine_funk("Väljatrüki nimi on ", "'Traceback'.")  # Funktsiooni teine_funk väljakutse.
---> 12 esimene_funk()

Cell In[7], line 10, in esimene_funk()
      9 def esimene_funk():
---> 10     teine_funk("Väljatrüki nimi on ", "'Traceback'.")

Cell In[7], line 7, in teine_funk(a, b)
      5 c = a + b  # Sõnede konkatenatsioon.
      6 print(c)   # --> Väljatrüki nimi on 'Traceback'.
----> 7 kolmas_funk(c)

Cell In[7], line 2, in kolmas_funk(a)
      1 def kolmas_funk(a):
----> 2     print(int(a))

ValueError: invalid literal for int() with base 10: "Väljatrüki nimi on 'Traceback'."

Valik muid olulisemaid vigu ja erisusi¶

SyntaxError (viga) Kasutaja unustab lõpetada sõne või muu andmetüübi defineerimise. Sõne puhul võime unustada jutumärgid. Sarnaselt võime unustada listi lõpetavad sulud, jmt.

In [8]:
print("Hello world!)  # NB! Jutumärgid.
  Cell In[8], line 1
    print("Hello world!)  # NB! Jutumärgid.
          ^
SyntaxError: unterminated string literal (detected at line 1)
In [9]:
a = [1, 2, 3  # NB! Sulud puudu.
  Cell In[9], line 1
    a = [1, 2, 3  # NB! Sulud puudu.
                                    ^
SyntaxError: incomplete input

IndentationError (viga) Kasutaja eksib taande vastu. Pythoni def, if, for ja muudes blokkides peab bloki sisu olema tähistatud taandega. Taane peab terve blokki ja ka ülejäänud mooduli piires olema sama. Lisaks veale IndentationError on olemas ka viga/erisus TabError mis tõstatakse kui taandamisel kasutatakse tühikuid ja tabulatsioone segamini. Pythonis peab taane olema tehtud alati ühetaoliselt terve mooduli (.py faili) ulatuses.

In [10]:
def func(a, b):
return a + b
  Cell In[10], line 2
    return a + b
    ^
IndentationError: expected an indented block after function definition on line 1
In [11]:
def func(a, b, c, d):
    var1 = a + b
     var2 = b + c
    return var1 + var2
  Cell In[11], line 3
    var2 = b + c
    ^
IndentationError: unexpected indent

IndexError Kasutaja püüab viidata indekseeritud andmetüübi elemendile kasutades liiga suurt või valet indeksi väärtust.

In [12]:
lst = ["Juulius", "on", "suur", "viga."]

print(lst.__len__())  # Sama mis len(lst).
print(lst[4])
4
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[12], line 4
      1 lst = ["Juulius", "on", "suur", "viga."]
      3 print(lst.__len__())  # Sama mis len(lst).
----> 4 print(lst[4])

IndexError: list index out of range
In [13]:
i = 0
while i < 4:
    i += 1
    print(lst[i])
on
suur
viga.
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[13], line 4
      2 while i < 4:
      3     i += 1
----> 4     print(lst[i])

IndexError: list index out of range

KeyError Kasutaja viitab sõnaraamatu väärtusele kasutades vigast või puuduvat võtit.

In [14]:
hinded = {
    "eesti keel": 4,
    "kehaline": 3,
    "mata": 2
    }

print(hinded["geograafia"])
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[14], line 7
      1 hinded = {
      2     "eesti keel": 4,
      3     "kehaline": 3,
      4     "mata": 2
      5     }
----> 7 print(hinded["geograafia"])

KeyError: 'geograafia'

Meeldetuletus: Sõnastike puhul on olemas viis kuidas eelnevas olukorras vältida erisuse ilmnemist. Seleks et vältida KeyErrorit kasuta meetodit get.

In [15]:
hinded = {
    "eesti keel": 4,
    "kehaline": 3,
    "mata": 2
    }

print(hinded.get("geograafia"))
None

NameError Deklareerimata/defineerimata muutuja või muu objekti nime kasutamine.

In [16]:
variable = 10
print(undeclared_variable + variable)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[16], line 2
      1 variable = 10
----> 2 print(undeclared_variable + variable)

NameError: name 'undeclared_variable' is not defined
In [17]:
def func_x(x):
    return x

func_y(2)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[17], line 4
      1 def func_x(x):
      2     return x
----> 4 func_y(2)

NameError: name 'func_y' is not defined

TypeError Kasutaja üritatab teha tegevusi kahe erineva andmetüübi esindaja vahel, mis kuidagi kokku ei käi.

In [18]:
var = "10"
print(var + 20)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[18], line 2
      1 var = "10"
----> 2 print(var + 20)

TypeError: can only concatenate str (not "int") to str
In [19]:
myStr = "100"
myResult = myStr >> 2
print("Result is:", myResult)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[19], line 2
      1 myStr = "100"
----> 2 myResult = myStr >> 2
      3 print("Result is:", myResult)

TypeError: unsupported operand type(s) for >>: 'str' and 'int'

Sama erisus tõstatub ka siis kui püüad teha funktsiooni väljakutset objektiga mis pole funktsioon.

In [20]:
myInt = 100
myInt()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[20], line 2
      1 myInt = 100
----> 2 myInt()

TypeError: 'int' object is not callable

Strateegiaid: Kuidas suhtuda ja mida teha vigade ja erisustega?¶

Erisustega töötamiseks ja nende haldamiseks on mitmeid strateegiaid:

  • Ei tee midagi, programm jookseb kokku.
  • Programm edastab kasutajale veateate ja lõpetab töö.
  • Programm analüüsib ja muudab/parandab sisendeid, sellega välditakse erisust.
  • Programm edastab teate ja annab kasutajale võimaluse vead parandada.
  • Programm üldistatakse nii, et erisust ei tekiks.
  • Haldad erisusi, lahtiseletatud allpool.

Erisuste haldamine¶

Pythonis saab hallata nii vigu kui ka erisusi kasutades sama lähenemist. See on ka põhjus miks tihtipeale praktikas ei eristata vigu erisustest. Erisuste haldamise eesmärgiks on soovimatu programmi töö peatuse vältimine. Erisusi haldame blokkis try, konstruktsiooni try/except/else/finally abil.

Süntaks:

try:
    <tegevus>
except Exception as <nimi>:       # Üldine/baas erisus.
    <Tegevus kui tõstatub erisus.>
    print(<nimi>)                 # Teavita kasutajat mis erisus tõstatati.
else:
    <Tegevus kui erisusi polnud.>
finally:
    <Tegevus juhuks kui ilmnes või ei ilmnend erisust.>

või

try:
    <tegevus>
except <erisus 1>:
    <Tegevus erisuse 1 jaoks.>
except <erisus 2>:
    <Tegevus erisuse 2 jaoks.>
except Exception as <nimi>:       # Üldine/baas erisus.
    <Tegevus kui tõstatus erisus.>
else:
    <Tegevus juhuks kui erisusi polnud.>
finally:
    <Tegevus juhuks kui ilmnes või ei ilmnend erisusi.>

Näide: Täiendame, üldistame ja analüüsime eelmainitud funktsiooni jaga ehk haldame erisusi.

In [21]:
def jaga(a, b):
    try:
        return a / b
    except Exception as e:   # Püüab kinni kõik erisused.
        print('Erisus:', e)  # Väljastan erisuse kirjelduse.

jaga(1, '0')
Erisus: unsupported operand type(s) for /: 'int' and 'str'
In [22]:
jaga(1, 0)
Erisus: division by zero
In [23]:
jaga(2, 2)
Out[23]:
1.0

Funktsiooni jaga üldistus. Haldame kõiki erisusi eraldi:

In [24]:
import math

def jaga(a, b):
    try:
        return a / b
    except TypeError:
        try:
            a, b = float(a), float(b)  # Proovin numbriks teisendada.
            return jaga(a, b)
        except ValueError as e:
            print('Ei teisene numbriks:', e)
            return None
        except TypeError as e2:
            print('Sellised sisendid pole lubatud:', e2)
            return None
    except ZeroDivisionError:
        print('Teade: Läheneb sellele suurusele või võimatu.')
        return a * math.inf  # Argumenti a kasutame märgi jaoks.

jaga(-1, 0)
Teade: Läheneb sellele suurusele või võimatu.
Out[24]:
-inf
In [25]:
jaga(1, '2')
Out[25]:
0.5
In [26]:
jaga(1, 'a')
Ei teisene numbriks: could not convert string to float: 'a'
In [27]:
jaga(0, 0)
Teade: Läheneb sellele suurusele või võimatu.
Out[27]:
nan
In [28]:
jaga(1, [2])
Sellised sisendid pole lubatud: float() argument must be a string or a real number, not 'list'

Erisuse käsitsi tõstmine, lausend raise¶

Lausendi raise abil saame tõstatada erisusi. Programmi peatamine on vahest vajalik ja mõistlik tegevus. Süntaksi näiteid:

In [29]:
raise FloatingPointError  # Süntaks: raise <erisuse nimi>
---------------------------------------------------------------------------
FloatingPointError                        Traceback (most recent call last)
Cell In[29], line 1
----> 1 raise FloatingPointError

FloatingPointError: 
In [30]:
raise FloatingPointError('Teade siia.')
---------------------------------------------------------------------------
FloatingPointError                        Traceback (most recent call last)
Cell In[30], line 1
----> 1 raise FloatingPointError('Teade siia.')

FloatingPointError: Teade siia.
In [31]:
if True:
    raise FloatingPointError('Teade.')
---------------------------------------------------------------------------
FloatingPointError                        Traceback (most recent call last)
Cell In[31], line 2
      1 if True:
----> 2     raise FloatingPointError('Teade.')

FloatingPointError: Teade.
In [32]:
try:
    prunt('Python')
except Exception:
    raise  # Erisust pole vaja eraldi täpsustada.
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[32], line 2
      1 try:
----> 2     prunt('Python')
      3 except Exception:
      4     raise

NameError: name 'prunt' is not defined

Näide: Tõstatame teadaoleva erisuse ZeroDivisionError funktsioonis jaga käsitsi.

In [33]:
import math

def jaga(a, b):
    try:
        if b == 0:
            raise ZeroDivisionError  # Tõstan käsitsi.
        else:
            return a / b
    except TypeError:
        try:
            a, b = float(a), float(b)
            return jaga(a, b)
        except ValueError as e:
            print('Ei teisene numbriks:', e)
            return None
        except TypeError as e2:
            print('Sellised sisendid pole lubatud:', e2)
            return None
    except ZeroDivisionError:  # Realt 6 liigume otse siia. 
        print('Realt 6 otse siia...')  # Defineerime siin käitumise mida soovime.
        return a * math.inf

jaga(-1, 0)
Realt 6 otse siia...
Out[33]:
-inf

Erisuse käsitis tõstmine ja lausend assert¶

Mäletatavasti saab lausend assert tõstatada erisuse AssertionError vt. eelmine loeng. Kasutame lausendit assert ja asendame funktsioonis jaga erisuse ZeroDivisionError erisusega AssertionError.

In [34]:
import math

def jaga(a, b):
    try:
        assert b != 0  # Tõstatab AssertionError'i, kui tingimuse bool on False.
        return a / b   # Kui b == 0 siis siia ritta ei jõutagi.
    except AssertionError:
        print('AssertionError Teade: Läheneb sellele suurusele või võimatu.')
        return a * math.inf
    except TypeError:
        try:
            a, b = float(a), float(b)
            return jaga(a, b)
        except ValueError as e:
            print('Ei teisene numbriks:', e)
            return None
        except TypeError as e2:
            print('Sellised sisendid pole lubatud:', e2)
            return None

jaga(-1, 0)
AssertionError Teade: Läheneb sellele suurusele või võimatu.
Out[34]:
-inf
In [35]:
jaga(1, '2')
Out[35]:
0.5
In [36]:
jaga(1, 'a')
Ei teisene numbriks: could not convert string to float: 'a'
In [37]:
jaga(0, 0)
AssertionError Teade: Läheneb sellele suurusele või võimatu.
Out[37]:
nan

Sisseehitatud erisuste hierarhia¶

Sisseehitatud erisused on defineeritud klassides, nende hierarhia on näidatud allpool. Lisainfo: https://docs.python.org/3/library/exceptions.html#exception-hierarchy

In [38]:
#help(Exception)

Erisuste hierarhia:

BaseException
 ├── BaseExceptionGroup
 ├── GeneratorExit
 ├── KeyboardInterrupt
 ├── SystemExit
 └── Exception                 <--- Üldine erisus Exception elab siin...
      ├── ArithmeticError
      │    ├── FloatingPointError
      │    ├── OverflowError
      │    └── ZeroDivisionError
      ├── AssertionError
      ├── AttributeError
      ├── BufferError
      ├── EOFError
      ├── ExceptionGroup [BaseExceptionGroup]
      ├── ImportError
      │    └── ModuleNotFoundError
      ├── LookupError
      │    ├── IndexError
      │    └── KeyError
      ├── MemoryError
      ├── NameError
      │    └── UnboundLocalError
      ├── OSError
      │    ├── BlockingIOError
      │    ├── ChildProcessError
      │    ├── ConnectionError
      │    │    ├── BrokenPipeError
      │    │    ├── ConnectionAbortedError
      │    │    ├── ConnectionRefusedError
      │    │    └── ConnectionResetError
      │    ├── FileExistsError
      │    ├── FileNotFoundError
      │    ├── InterruptedError
      │    ├── IsADirectoryError
      │    ├── NotADirectoryError
      │    ├── PermissionError
      │    ├── ProcessLookupError
      │    └── TimeoutError
      ├── ReferenceError
      ├── RuntimeError
      │    ├── NotImplementedError
      │    └── RecursionError
      ├── StopAsyncIteration
      ├── StopIteration
      ├── SyntaxError
      │    └── IndentationError
      │         └── TabError
      ├── SystemError
      ├── TypeError
      ├── ValueError
      │    └── UnicodeError
      │         ├── UnicodeDecodeError
      │         ├── UnicodeEncodeError
      │         └── UnicodeTranslateError
      └── Warning
           ├── BytesWarning
           ├── DeprecationWarning
           ├── EncodingWarning
           ├── FutureWarning
           ├── ImportWarning
           ├── PendingDeprecationWarning
           ├── ResourceWarning
           ├── RuntimeWarning
           ├── SyntaxWarning
           ├── UnicodeWarning
           └── UserWarning

Erisusete haldamise kasutamisest¶

Üldised soovitused erisustega töötamisel:

  • Oht $-$ erisused peidavad programmi vigu.
  • Kui võimalik ära kasuta, eelista programmi üldistamist.
  • try-blokk peab olema minimaalne.
  • Näita olemasolevat erisuse infot (inimloetav sisu kirjeldus) või kirjuta see ise.

Näide: Inimloetavate teadete loomine.

In [39]:
try:
    0 / 0
except Exception as info:
    print('Minu inimloetav teade siia.')
    print(info, type(info))  # Midagi taolist.
Minu inimloetav teade siia.
division by zero <class 'ZeroDivisionError'>
  • Väljasta erisus käsitsi kasutades lausendit raise, programmi peatamine ei pruugi olla halb mõte.

Näide: Inimloetava teate genereermine kasutades lausendit raise blokis except.

In [40]:
try:
    0 / 0
except Exception as info:
    print('Tekkis erisus:', type(info).__name__,':', info) # Kasuta erisuse nime.
    raise  # Siin pole vaja erisust täpsustada.
Tekkis erisus: ZeroDivisionError : division by zero
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[40], line 2
      1 try:
----> 2     0 / 0
      3 except Exception as info:
      4     print('Tekkis erisus:', type(info).__name__,':', info) # Kasuta erisuse nime.

ZeroDivisionError: division by zero

Silumine (debugging)¶

Silumine on tarkvaraarenduse protsess, mille käigus leitakse ja parandatakse programmikoodivead, mis takistavad programmi korrektset toimimist. Silumist tuleb eristada silurist, mis on tarkvaraprogramm või -moodul, mida kasutatakse silumiseks. Allpool arutame silumist (täpsemalt käsitsi silumine, avastuslik silumine) tarkvara dünaamilise prototüüpimise kontekstis.

Prototüüpimine $-$ (meie kursuses siiamaani Pythoni lähtekoodi kirjutamist saab kategoriseerida nii) väiksemate programmide loomine, üksikute programmi osade loomine, loodud programmiosad erinevad lõpp-produktist, jne. Kood on pidevas muutumises $-$ kirjutad koodi, interpreteerid koodi, uurid tulemst ja kordad eelnevaid tegevusi mitmeid kordi.

Millest, silumise kontekstis, siin kursusel ei räägita:

  • Üksuste testimine (unit testing, automated testing). Uuri teemat iseseisvalt.
  • Kõikvõimalikud integratsiooni- ja süsteemitestid.
  • Koodi refaktoriseerimine (code refactoring) $-$ koodi mittefunktsionaalse osa silumine.
  • Süntaksi süvaanalüüs (lintering: PyLint/pyflakes) sisseehitatud Pycharm ja Spyder IDE'sse.
  • Fuzzing $-$ testimine kasutades juhuslikult genereeritud sisendandmeid.
  • Delta silumine (delta debugging).
  • Post mortem silumine (post mortem debugging mode: pdb võimaldab seda).

Üksuste testimine (unit testing)¶

Uuri teemat iseseisvalt. See pole keeruline kui oskad kasutada lausendit assert, vt. eespool ja Loeng 8.

Üksuste testimise puhul testitakse ühete konkreetset üksust koodist. Üksus võib olla nii funktsioon kui ka terve funktsioonidest (meetoditest) koosnev objekt. Klassis defineeritud objektide puhul testitakse seal olevaid meetodeid ja vaadatakse, kas need mõjutaksid objekti õigesti. Funktsioonide puhul testitakse, et see annaks õige väljundi erinevate argumentide korral. Selleks, et programmeerija ei peaks kogu aeg funktsioone käivitama ja tulemust vaatama (näiteks kasutades funktsiooni print), kasutatakse spetsiaalseid testimise tööriistu: moodulid unittest (Rohkem võimalusi. Peaksid olema Anaconda installatsiooniga kaasas.) ja pytest (lihtsam).

IDEs Spyder kasuta pluginat spider-unittest, mis pole arenduskeskkonda vaikimisi lisatud. Installeeri plugin Anaconda konsoolis kasutades käsku:

conda install spider-unittest

Vigade ja erisuste tüübid¶

Vead jagatakse kolme tüüpi:

  • Süntaksiviga (compile time error) $-$ Pythoni interpreteerija ei saa koodist aru ja seetõttu ei hakkata programmi üldse käivitama, eksitakse keele kirjutamise reeglite vastu.
  • Täitmisaegne viga (runtime error) $-$ programm käivitati, aga mingi konkreetse käsu täitmine ebaõnnestub. Vigaseks käsuks võis olla näiteks nulliga jagamine, valesti kirjutatud funktsiooninime kasutamine, olematu faili lugemine, vale andmetüübiga sisendandmed, vms.
  • Semantikaviga (semantic error) või lihtsalt loogikaviga $-$ kõik on Pythoni seisukohast korrektne (st. mingit veateadet või erisust ei tõstatata), aga programm ei tee seda, mis programmeerija silmas peab. Näiteks oled unustanud segatehete tehete prioriteetsused (Loeng 3) või LEGB reegli (Loeng 6).

Eelloetletud vead on järjestatud silumise raskuse järjekorras.

Abimaterjalid ja tööriistad¶

Üldised abistavad tööriistad mida ei tasu allahinnata:

  • Pythoni erisuse traceback.
  • IDE'sse sisseehitatud toed (Python Docs, DocString'id, jne.). Abiinfo järgivaatamine: Ctrl + I.
  • IPythoni konsooli kasutamine:

IPythoni konsoolis meile juba teadaolevate üldiste abimaterjalide järelevaatamise viisid. Meeldetuletus:

In [41]:
#print?      # iPythoni konsoolis.
In [42]:
#help('if')  # Definitsioonide kuvamine.
In [43]:
#help(str)   # Meetodid, funktsioonid ja nenede kirjeldused.
In [44]:
#dir(str)    # Meetodite ja atribuutide loetelu.
In [45]:
#locals()    # Lokaalne nimeruum.
In [46]:
#globals()   # Globaalne nimeruum.
  • Internet ja Googeldamine, Näiteks: kolmandate osapoolte teekige kodulehed https://matplotlib.org/ jne.
  • PythonDocs server veebilehitsejas. Sisesta konsooli python -m pydoc -b.
  • Loe imporditud kolmandate osapoolte lähtekoodi ennast, võib sisaldada vihjeid probleemide lahnedustele. Spyderis: Kursor objekti kohale ja Ctrl + G või Ctrl + hiire klikk (lugemine võimalik ainult siis kui lähtekood pole ettekompileeritud kujul).

Millist silurit kasutada?¶

  • pdb $-$ Python debugger (python standardteegis alati olemas: ei vaja graafilise kasutajaliidesega IDE'd), ei käsitle selles kursuses.
    • Käsurida jupyter: debug, %debug, pdb, või %pdb, sama käsk suleb siluja.
    • Moodulis: import pdb; pdb.set_trace().
  • IPdb Interactive Python debugger (Spyder'sse sisseehitatud).
  • Spyder (graafiline) töötab koos IPdbga.
  • PyCharm'i Visual studio ja muude IDE'de graafilised silurid.

Silumine Spyder'i iPythoni konsoolis¶

ipdb: valik käske IDE Spyder konsoolis:

  • n(ext), teosta käesolev rida ja liigu järgmisesse ritta.
  • s(tep), sisene funktsiooni.
  • l(ist), näita kolm rida enne ja peale käesolevat rida.
  • b(reakpoint) <rida>
  • b(reakpoint) <rida>, <tingimus>
  • b(reakpoint) <funktsiooni nimi>
  • c(obtinue), kuni järgmise breakpoint'ni või lõpuni.
  • q(uit), välju silurist.
  • r(un), kuni funktsiooni return lausendini.

Kuidas siluda, soovitusi¶

Tea kuidas kodeerida ja tea süntaksi reegleid ning lihtsalt kirjuta veavabalt. Kui see ei õnnestu, või töötad kellegi teise poolt kirjutatud vigase koodiga, siis silu. Järgnevaid soovitusi rakenda esitatud järjekorras.

Soovitused:¶

Soovitus 0: Interpreteeri programmi lähtekood $-$ jooksuta programm läbi.

Soovitus 1: Loe erisuse teadet (traceback), tegutse vastavalt (tea kuidas).

Soovitus 2: Loe lähtekoodi õigelt reanumbrilt, avasta sealt viga või erisus.


Korrates Soovitusi 0, 1 ja 2 avastad ja parandad enamuse süntaksi ja täitmisaegsetest vigadest.

Soovitus 3: Funktsioon print $-$ pole hea tööriist aga kuidagi suudab 90% juhtudest sul vead avastada ja ka ära parandada.

  • Kasuta muutujate ja objektide väärtuste järgivaatamiseks. Kasuta ka muutujate lehitseja akent.
  • Kontrolli kas teatud real juhtub see mis seal peab juhtuma.
  • Kas teatud real olev funktsiooni print väljakutse teostub enne erisuse tõstatumist.

   Kasuta ettevaatlikult:

  • Liiga palju print lauseid risustavad konsooli väljundit ja segavad selle lugemist.
  • Liiga palju print lauseid segavad lähtekoodi enda lugemist.

Järgisena tulevad semantika- ehk loogikavead. Nüüd alles algab tõsine silumine:


No description has been provided for this image
Joonis 1. Teadusliku meetodi diagramm.

Soovitus 4: Rakenda "teaduslikku meetodit", mille diagramm on kujutatud Joonisel 1, me tahame ju midagi teada saada $-$ vea olemust.

  • Hüpotees ja selle ümberlükkamine.
  • Testi hüpoteese teadlikult, muutes koodi ja ennustades tulemust.
  • Dokumenteeri juba tehtut (või vähemalt ära unusta juba tehtut), ole sihikindel $-$ "teaduslik".

Soovitus 5: Lihtsusta probleemi püstitust, minimeeri sisendid, rakenda Punkti 4 uuesti (käsitsi ühiktestimine, käsitsi unit testimine).

  • Minimal Reproducible Example (MRE): https://en.wikipedia.org/wiki/Minimal_reproducible_example

No description has been provided for this image   No description has been provided for this image   No description has been provided for this image
Joonis 2. Vasak: Kummist part ehk ujuv skulptuur "Spreading Joy Around the World", autor Florentijn Hofman.
Keskmine, Parem: Programmeerija seletab enda koodi kummist pardile.

Soovitus 6: Programmeerijate seas levinud meem: seleta probleem kummist pardile (rubber duck debugging), vt. Joonis 2: Lisainfo siit: https://en.wikipedia.org/wiki/Rubber_duck_debugging
Mitte segi ajada mõistetega duck typing (https://en.wikipedia.org/wiki/Duck_typing) või duck test (https://en.wikipedia.org/wiki/Duck_test).

  • Seleta probleem kummist pardile, lapsele või koerale.
  • Lihtsustatult (ümber)seletatud probleem kipub ennast ise lahendama.
  • Seleta reakaupa. Kirjelda lahti iga rea sisu, -sisendid ja oodatav väljund.
  • Vali hea töögrupp ja kolleegid kellega koos töötada... Ideede vahetamine, suhtlemine, juhendamine ja üksteise toetamine.

Soovitus 7: Korrasta lähtekood, eemalda surnud kood, korrasta väljundi esitamise viis, jne.

  • Aitab ülevaadet hoida.

Soovitus 8: Kasuta blokke assert ja try/except konstruktsioone (käsitsi ühiktestimine, käsitsi unit testimine).


Alles nüüd kasutame silurit.

Soovitus 9: Silurid (vt. eelolevat teksti.)

  • Python debugger, pdb või IPdb silurid.
  • Graafilise kasutajaliidesega silurid (Spyder, PyCharm, Visual Studio, jt.)

Soovitus 10: Logide loomine ja nende analüüs.

  • Moodul logging, (meie kursusel seda ei käsitleta).

Soovitus 11: Puhka ja alusta uuesti.


Vähetõenäoline:

Soovitus 12: Kui kõik eelnev ei tööta: Probleem võib olla IDE's, operatsiooni süsteemis, arvuti raudvaras, distributsiooni installatsioonis, jms.

  • Taaskäivita Python'i kernel.
  • Taaskäivita masin.

Versioonikontroll ja koostöö teistega¶

Versioonikontrolli tarkvara võimaldab arendajatel salvestada koodis tehtud muudatused. Versioonikontrollisüsteemid võimaldavad mitmel arendajal töötada ühe projekti kallal. Salvestada tehtud muudatusi viisil mis võimaldab arendajatel projekti arengut jälgida. Versioonikontrollisüsteeme (VCS) on kolme tüüpi: kohalik, tsentraliseeritud ja hajutatud.

No description has been provided for this image
Joonis 3. Git'i logo.

Git (https://git-scm.com) on üks hajutatud versioonikontrollisüsteemi tarkvaradest, mis võimaldab arendajatel jälgida oma arvutifailide muudatusi ja töötada koostöös teiste arendajatega. Selle lõi operatsioonisüsteemi Linux looja Linus Torvalds 2005. aastal, et võimaldada teistel arendajatel anda oma panus Linuxi operatsioonisüsteemi tuuma arendamisesse. Joonis 3 kujutab Git'i logo.

No description has been provided for this image   No description has been provided for this image
Joonis 4. GitHub'i logod.

GitHub (https://github.com) on veebirepositoorium, mis pakub kõiki Gitis leiduvaid hajutatud versioonikontrolli ja lähtekoodihalduse (SCM) funktsioone. Seda kasutatakse tavaliselt koostöös Gitiga muuhulgas võimaldab tarkvara arendajatel oma koodi veebis teistega jagada ja teha koostööd teiste arendajatega. Joonis 4 kujutab GitHub'iga seotud logosid.

Info kasutamise kohta: https://realpython.com/python-git-github-intro/

















☻   ☻   ☻