5.1. Ohjelman suorituksen tarkastelua
Tutkaillaan ohjelman suoritusta ja tutustutaan kahteen Pythonin työkaluun. Moduli trace
näyttää muun muassa
ohjelman suorittamat lauseet (ks. The Python Standard Library: 27.6. trace — Trace or track Python statement execution). Moduli pdb
puolestaan on Pythonin interaktiivinen
debuggeri eli virheenjäljitin (ks. The Python Standard Library: 27.3. pdb — The Python Debugger).
Ohjelman sekventiaalinen suoritus
Kuten tähän mennessä on jo varmaan tullut selväksi, ohjelman suoritus tapahtuu lause kerrallaan edeten ohjelmatekstissä ylhäältä alaspäin. Ehtolauseissa ohitetaan epätosia vaihtoehtoja vastaava koodi, toistolauseissa hypätään takaisin lauseen alkuun aina kun toistoehto on voimassa ja metodikutsuissa hypätään kutsuttavaan koodiin. Tätä kutsutaan sekventiaaliseksi suorittamiseksi. Ohjelman suoritus muodostaa sekvenssin lauseita tai käskyjä. Perehdytään kahteen työkaluun, joilla voi tutkia ohjelman suoritusta. Käytetään seuraavaa esimerkkifunktiota (iteratiivinen_kertoma.py.), joka laskee annetun luvun kertoman iteratiivisesti.
def kertoma(n):
tulos = 1
while n > 0:
tulos = n*tulos
n -= 1
return tulos
kertoma(4)
Pythonin kirjaston moduli trace
sisältää jäljittimen, jolla saamme näkyviin kaikki suoritetut lauseet.
> python3 -m trace -t iteratiivinen_kertoma.py
--- modulename: iteratiivinen_kertoma, funcname: <module>
iteratiivinen_kertoma.py(1): def kertoma(n):
iteratiivinen_kertoma.py(8): kertoma(4)
--- modulename: iteratiivinen_kertoma, funcname: kertoma
iteratiivinen_kertoma.py(2): tulos = 1
iteratiivinen_kertoma.py(3): while n > 0:
iteratiivinen_kertoma.py(4): tulos = n*tulos
iteratiivinen_kertoma.py(5): n -= 1
iteratiivinen_kertoma.py(3): while n > 0:
iteratiivinen_kertoma.py(4): tulos = n*tulos
iteratiivinen_kertoma.py(5): n -= 1
iteratiivinen_kertoma.py(3): while n > 0:
iteratiivinen_kertoma.py(4): tulos = n*tulos
iteratiivinen_kertoma.py(5): n -= 1
iteratiivinen_kertoma.py(3): while n > 0:
iteratiivinen_kertoma.py(4): tulos = n*tulos
iteratiivinen_kertoma.py(5): n -= 1
iteratiivinen_kertoma.py(3): while n > 0:
iteratiivinen_kertoma.py(6): return tulos
--- modulename: trace, funcname: _unsettrace
trace.py(80): sys.settrace(None)
Voimme tarkastella suoritusta myös Pythonin pdb-debuggerilla. Tätä varten ladataan moduli pdb
sekä omasta
modulistamme funktio kertoma
. Käynnistetään debuggeri komennolla pdb.run('kertoma(4)')
, jonka jälkeen voimme
askeltaa suoritusta komennolla s
(eli step
) ja asettaa debuggerin tulostamaan muuttujan arovn aina kun se
muuttuu (esim. display tulos
).
> python3
Python 3.4.2 (default, Jan 7 2015, 11:54:58)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pdb
>>> from iteratiivinen_kertoma import kertoma
>>> pdb.run('kertoma(4)')
> <string>(1)<module>()
(Pdb) s
--Call--
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(1)kertoma()
-> def kertoma(n):
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(2)kertoma()
-> tulos = 1
(Pdb) display n
display n
display n: 4
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(3)kertoma()
-> while n > 0:
(Pdb) display tulos
display tulos
display tulos: 1
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(4)kertoma()
-> tulos = n*tulos
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(5)kertoma()
-> n -= 1
display tulos: 4 [old: 1]
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(3)kertoma()
-> while n > 0:
display n: 3 [old: 4]
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(4)kertoma()
-> tulos = n*tulos
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(5)kertoma()
-> n -= 1
display tulos: 12 [old: 4]
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(3)kertoma()
-> while n > 0:
display n: 2 [old: 3]
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(4)kertoma()
-> tulos = n*tulos
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(5)kertoma()
-> n -= 1
display tulos: 24 [old: 12]
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(3)kertoma()
-> while n > 0:
display n: 1 [old: 2]
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(4)kertoma()
-> tulos = n*tulos
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(5)kertoma()
-> n -= 1
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(3)kertoma()
-> while n > 0:
display n: 0 [old: 1]
(Pdb) s
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(6)kertoma()
-> return tulos
(Pdb) s
--Return--
> /Users/enu/y2/y2-course-material/k05/iteratiivinen_kertoma.py(6)kertoma()->24
-> return tulos
Debuggerin tarjoamiin komentoihin kannattaa perehtyä, sillä debuggerilla on usein helpointa selvittää, miksi ohjelma toimii väärin.
Kutsupino
Jotta Python-tulkki voisi suorittaa ohjelmaa, tarvitsee se joitain aputietorakenteita. Yksi näistä on kutsupino (engl. call stack) eli suorituspino, johon talletetaan jokaista funktio/metodikutsua kohti yksi pinokehys (engl. stack frame) eli *aktivaatiotietue (engl. activation record). Pinokehys sisältää tiedon kutsuttavasta funktiosta sekä sen parametreista ja lokaaleista muuttujista (eli siis nimiavaruuden).
Tämänkaltaista kutsupinoa käytetään kaikissa käyttöjärjestelmissä ohjelmien suorittamiseen. Tarkemmin aiheeseen voi tutustua esim. Wikipedian artikkelissa Call stack.
Tutustutaan kutsupinoon käyttäen esimerkkinä kertoman rekursiivisesti laskevaa ohjelmaa rekursiivinen_kertoma.py. Ohjelmaan on lisätty apufunktio show_stack()
, joka
näyttää pinon sisällön, kun kertoma
-funktio saavuttaa sisimmän rekursiotason.