1. Materiaali: Kosmisten muuttujien arvoitus¶
Mitä täällä oikein tapahtuu?¶
Ohjelmoinnin alkeet kurssin nimenä tarkoittaa, että kurssilla opitaan ohjelmointia ilman minkäänlaisia esitietovaatimuksia. Tie on ajoittain kivinen, mutta suuret saavutukset toivon mukaan kompensoivat nähtyä vaivaa. Viimeistään aloitusviikon harjoitukset antavat riittävät taidot ohjelmoinnin opettelun aloittamiseen. Opit asentamaan ja käynnistämään oikeat sovellukset sekä kirjoittelemaan laskutoimituksia
interaktiiviseen Python-tulkkiin
. Saatpa myös tietää, mikä tuo mystinen tulkki oikein on. Kooditiedostostakin
muodostuu alustavasti tuttu käsite. Ohjelmoinnin oppimisen haastavuus riippuu monesta tekijästä, mutta sellaista tekijää ei olekaan, joka estäisi oppimisen. Oppimista tehostaa erityisesti, jos mukana on tiettyä tavoitteellisuutta. Jo tässä vaiheessa onkin siis hyvä miettiä, mihin juuri sinä voisit käyttää ohjelmointia. Jotakuinkin kaikilla aloilla ja jopa monissa harrastuksissa voi jollain tapaa hyödyntää ohjelmointiosaamista. Ohjelmointitaito johtaa yllättäviin mahdollisuuksiin täysin odottamattomissa tilanteissa. Mikään ei kuitenkaan vedä vertoja sille motivaatiolle, mikä syntyy henkilökohtaisista päämääristä. Mitä sinä voisit ohjelmoida?
Kurssin puolesta me voimme vain tarjota kalpean päämäärän varjon: kaikki kurssilla kiteytyy lopputyöhön. Jos muuta tarkoitusta ohjelmoinnille ei juolahda mieleesi, pidä ainakin valitsemasi lopputyöaihe mielessä, kun kohtaat uusia haasteita ja opit uusia asioita. Joku aamu saatat herätä ja kokea mystisen valaistumisen siitä, miten miinaharava toimii konepellin alla, miten RLC-piiriä voidaan ratkoa ohjelmallisesti tai kuinka mittaustulokset taittuvat nätille käyrälle. Sillä hetkellä kannattaa muistaa, mistä olet lähtenyt liikkeelle, ja todeta miten paljon olet saavuttanut tässä lyhyessä ajassa. Aiheisiin kannattaa siis tutustua vaikka heti.
Asia selvä, sensei. Kerrotko jo, mitä se ohjelmointi on?¶
Ohjelmointi ei ole pelkkää
koodin
kirjoittamista. Koodin kirjoittaminen on vain helpoiten havaittava ilmentymä ohjelmoinnista. Voit harrastaa erinäisiä ohjelmointiaktiviteetteja täysin salaa. Vasta kun alat kirjoittaa koodia, aiheeseen vihkiytymättömät ihmiset ympärilläsi tajuavat mitä olet tekemässä. Tässä vaiheessa on tietenkin myöhäistä, sillä suunnitelmasi maailmanvalloitukseen on jo täysillä käynnissä. Mitä muuta ohjelmoija sitten tekee kuin kirjoittaa koodia? Yleisesti voidaan sanoa, että hän ratkaisee
ongelmia
. Ongelma on tässä kontekstissa mikä tahansa tarve, joka voidaan tyydyttää ohjelmoimalla – oli kyseessä sitten tein-itse-ja-säästin miinaharavan tarve, tai tarve postailla hassuja kuvia internetiin muiden ihmisten peukutettavaksi. Kumpikaan ei varsinaisesti ole ongelma maailmassa, mutta kun tarve on valittu ohjelmointityön kohteeksi, voidaan sanoa, että siitä tulee ohjelmointiongelma. Ohjelmointiongelman alustava ratkaisu on puolestaan suunnitelma siitä, mitä varsinaisen koodin tulisi tehdä. Tässä työvaiheessa
ratkaisumalli
saattaa olla olemassa ja työn alla ainoastaan ohjelmoijan päässä ja kenties myös paperilla. Ratkaisumallin tuottaminen on ensimmäinen näkymätön ohjelmointaito. Ratkaisumallia tuottaessa on keskeistä tietää, mitä koodilla voi tehdä, mutta vielä ei tarvitse tietää tarkkoja yksityiskohtia siitä, miten koodi kirjoitetaan. Sen sijaan on tarpeen ymmärtää ohjelmoinnin käsitteet. Nämä vastaavat kysymyksiin siitä, miten ohjelmien etenemistä yleisesti hallitaan ja miten ohjelmat mallintavat tietoa. Niitä oppiessa on hyvä muistaa, etteivät ne ole pelkästään Pythonin ominaisuuksia. Sen sijaan ne ovat läsnä kaikissa ohjelmointikielissä tavalla tai toisella.
Ratkaisumallin
käsitteellistäminen muodostaa siis toisen näkymättömän ohjelmointitaidon. Tämän prosessin jälkeen voidaan viimeinkin puhua varsinaisesta näkyvästä taidosta, eli koodin kirjoittamisesta. Koodi itsessään on vain
syntaksia
, jolla ohjelmoija kuvaa kehittämänsä ratkaisumallin yksiselitteisesti tietokoneelle. Kaikkeen muuhun verrattuna se on melkeinpä vain muodollisuus. Pelkästään kirjoittamalla koodia ei siis opi ohjelmoimaan. Suo, muuttuja ja koodari¶
Osaamistavoitteet: Tämän osion läpikäytyäsi pitäisi muuttujan olla tuttu käsite. Muuttujien käyttö asioiden muistamiseen koodatessa tulee myös tutuksi. Lisäksi muuttujien syvempi luonne ja liittyminen tietokoneen muistiin alkaa vähitellen avautua. Sijoitusoperaattori tulee tutuksi. Samoin muuttujien nimeämiseen liittyvät säännöt ja suositukset.
Johdatus muuttujien maailmaan¶
Ensimmäisenä opittavana ohjelmoinnin käsitteenämme on muisti. Asioiden muistaminen useamman kuin yhden operaation jälkeen tulee tarpeeseen heti, kun ratkotaan yhtään mitään monimutkaisempaa ongelmaa. Ohjelmoinnissa tiedon pitämiseen muistissa käytetään
muuttujia
. Pythonissa Muuttuja luodaan sijoittamalla siihen jokin arvo. Muuttujaan sijoitus tapahtuu yhtäsuuruusmerkillä siten, että muuttuja on vasemalla ja sijoitettava arvo oikealla:x = 1337
Ohjelmoinnissa tallentaminen tapahtuu tietokoneen muistiin. Merkittävä ero ihmisen ja tietokoneen muistissa on se, että ihminen tietää mikä merkitys omassa muistissa olevilla asioilla on, kun taas tietokone ei. 1337 on vain numero jossain muistin syövereissä. Sen tallennettuaan tietokone ei suoraan osaa vastata pyyntöön "anna minulle aiemmin ratkaistu x:n arvo". Se kuitenkin tietää missäkin muistipaikassa on, joten se osaa kyllä kertoa, jos ohjelmoija pyytää tietoa siitä, mitä tiettyyn muistiosoitteeseen on tallennettu. Ohjelmoijan ei sentään tarvitse osata muistiosoitteita ulkoa tiedon muistista noutamista varten. Sitä varten on olemassa muuttujat – tässä tapauksessa siis x.
Muuttujan käsittäminen¶
Muuttujan
käsitteen voi ymmärtää hyvin monella tavalla. Pohjimmiltaan kyse on ohjelmoijan ja Python-tulkin
välisestä sopimuksesta siitä, että tietty sana (esim. x) viittaa
juuri tiettyyn paikkaan tietokoneen muistissa. Sopimuksen tuloksena molemmat osapuolet pystyvät käsittelemään tallennettua tietoa tavalla, joka on luontevaa: ohjelmoija voi kutsua asioita kuvaavilla nimillä, kun taas tietokoneen puolella puhutaan muistiosoitteista. Asian voi myös ilmaista yksinkertaisemmin toteamalla, että esimerkissä x = 1337
x tarkoittaa tämän rivin suorituksen jälkeen arvoa 1337 - vaikkei tämä olekaan täysin tarkka ilmaisu.Koska ohjelmoidessa usein puhutaan muuttujan sisällöstä, seuraava väite voi olla hieman hämmentävä: muuttuja ei tosiasiallisesti sisällä mitään. "Muuttujan sisältö" on vain arkikielinen tapa puhua siitä
arvosta
, joka on tietokoneen muistissa muuttujan viittaamassa
paikassa. Muuttujan
kaltaisia asioita ovatkin vaikkapa kappaleen otsikko kirjan sisällysluettelossa tai kaupungin nimi kartalla. Kumpikaan ei sisällä sitä, mihin ne viittaavat; ne ainoastaan kertovat, mistä se löytyy. Kartta itsessään ei tiedä mitä "Oulu" merkitsee sitä etsivälle. Se tietää vain, että sellainen nimi on annettu tietyissä koordinaateissa olevalle maantieteelliselle alueelle.Jos siis rivi
x = 1337
tarkoittaa, että otetaan käyttöön nimi x viittaamaan muistipaikkaan, johon juuri tallennettiin arvo 1337, mitä seuraavat rivit tarkoittavat? y = x
x = 4451
Ensimmäisellä rivillä annetaan toinen nimi x:n
viittaamalle
arvolle. Tämän jälkeen sekä x että y viittaavat samaan arvoon
, joka on siis 1337. Kyseessä ei ole nimen muuttaminen, koska tämän jälkeen molemmat, sekä x että y, ovat olemassa. Kyseessä ei myöskään ole arvon kopiointi, koska yksi 1337 arvo on muistissa. Toisella rivillä määritellään uusi arvo 4451, joka sijoitetaan johonkin uuteen muistipaikkaan. Tämän jälkeen määritetään, että x viittaakin nyt tähän uuteen muistipaikkaan ja sen sisältämään arvoon. Lopputuloksena siis meillä on edelleen x ja y, mutta niiden arvot ovat nyt vastaavasti 4451 ja 1337. Alla oleva animaatio selventää arvojen ja nimien suhdetta.Huomioitavaa on, että vaikka
muuttujaan
sijoittaminen tapahtuu =-merkillä, se ei ole sama asia kuin matemaattinen yhtäsuuruus. Erityisen oleellista on se, mitä on merkin vasemmalla ja mitä oikealla puolen. Vasemmalla on siis aina muuttuja (tai vastaava), mihin sijoitetaan, ja oikealla on aina arvo, joka sijoitetaan. Jos yrittää tehdä toisin päin...1337 = x
...tapahtuu kauheita:
SyntaxError: can't assign to literal
Eli koodissa on
syntaksivirhe
, koska literaaliarvoon
(esim. kokonaisluku) ei voi sijoittaa mitään. Merkin vasemalla puolella ei voi myöskään suorittaa operaatioita
...x + 3 = 5
...koska jälleen tapahtuu kauheita:
SyntaxError: can't assign to operator
Tällä kertaa
virheviesti
on vähän hämmentävämpi, mutta viittaa siis siihen, että Python olettaa, että haluamme sijoittaa x + 3 operaatioon
. Virheviestien tulkintaa harrastetaan tarkemmin materiaalin loppupuolella. Tiivistettynä =-merkin vasemmalle puolelle kelpaavat vain
muuttujien
nimet. Kaiken muun pitäisi tapahtua merkin oikealla puolen. Myöhemmin tosin nähdään vielä pari erilaista asiaa jotka voidaan laittaa vasemalle puolelle. Siitä, että sijoitusoperaattorin vasemalla puolella on oltava vain ja ainoastaan muuttujanimi, saavutaan myös seuraavaan päätelmään. Jos muuttujan nimi esiintyy missään muualla kuin sijoitusoperaattorin
vasemmalla puolella, silloin viitataan muuttujan arvoon (eli siihen muistissa olevaan arvoon, johon muuttuja itse viittaa). Kun seuraavaa lauseketta
puretaan, voidaan muuttujan
paikalle kuvitella sen arvo
. Arvo nimittäin haetaan muistista muuttujan paikalle ennen rivin kuvaaman lauseen suorittamista. Suoritusjärjestyksestä tulee huomioida, että kaikki, mitä on =-operaattorin oikealla puolella, suoritetaan kokonaisuudessaan, ennen kuin muuttujaan vasemmalla puolella sijoitetaan yhtään mitään. x = y + 4451
Koska y:n
arvoksi
jäi aiemman pyörittelyn ansiosta 1337, x:n arvoksi tulisi 5788. Tässä siis y:n arvo haetaan muistista ja lisätään siihen 4451, minkä jälkeen lopputulos tallennetaan uuteen muistipaikkaan ja sovitaan, että nimi x viittaa sinne. x:n vanha arvo jää hengailemaan muistipaikkaansa unohdettuna kunnes sen päälle kirjoitetaan jotain. Kun nyt näitä
sijoituksia
ja muita operaatioita on tässä muutamaan otteeseen kirjoiteltu, voidaan saman tien sopia niihin liittyvä tyylisääntö
: operaattorin
(siis sen merkin) ja operandien
(tässä tapauksessa nimien tai lukuarvojen) väliin kuuluu aina yksi välilyönti. Tämä sen vuoksi, että muuten koodi näyttää liian tunkkaiselta. Se sattuu olemaan myös linjassa Pythonin virallisen tyylistandardin kanssa. Eli esimerkkien mukaisesti, välilyönti aina ennen operaattoria sekä sen jälkeen. Tehtävien tarkastus pitää myös tästä tyylisäännöstä kiinni. Muuttujan
arvoa
voi myös niin sanotusti päivittää. Jos esimerkiksi yhdessä muuttujassa on tallessa joukkueen maalit jalkapallo-ottelussa ja joukkue tekee maalin, koodissa voisi olla seuraavanlainen rivi tilanteen käsittelyä varten: maalit = maalit + 1
Tällä rivillä siis haetaan maalit-muuttujan arvo, lisätään siihen yksi, ja uudelleenmääritellään maalit-muuttuja
viittaamaan
tähän uuteen tulokseen. Tälle operaatiolle
on olemassa myös lyhennetty syntaksi
, jossa maalit-nimeä ei tarvitse kirjoittaa kahdesti:maalit += 1
Sama syntaksilyhennys toimii kaikilla muillakin
operaattoreilla
.Muuttujien nimeäminen¶
Muuttujien
nimeämisessä on sääntöjä, joiden suhteen Python on ehdoton. Nimissä saa käyttää ainoastaan kirjaimia, numeroita sekä alaviivaa. Nimi ei kuitenkaan saa alkaa numerolla. Alaviivalla alkavilla nimillä on erityismerkitys, minkä vuoksi niitäkään ei ole suotavaa käyttää alkeiskurssin kontekstissa. Näiden sääntöjen puitteissa voit nimetä muuttujasi miten haluat. Hassunhauskan teekkarihuumorin viljelyä koodiin kannattaa kuitenkin välttää. Alla on esitelty muutamia sallittuja, hyvän nimeämistavan mukaisia muuttujia:ottelut = 5
pelaaja1 = "Hessu"
pelaajan_pisteet = 4.8
Kuten muistamme, muuttujat ovat sopimuksia ihmisen ja koneen välillä siitä, mitä milläkin muuttujan nimellä tarkoitetaan. Koska tietokone ei juurikaan välitä nimistä, sinulla on mahdollisuus valita nimet siten, että helpotat omaa työtäsi mahdollisimman paljon. Konetta ei kiinnosta mikä on numeron 1337 merkitys, mutta sinua todennäköisesti kiinnostaa. Se kannattaa siis nimetä siten, että myöhemminkin koodia lukiessa on selvää mitä se merkitsee. Jos kyseessä on esimerkiksi matkan pituus, x ei ole kuvaava nimi. Usein myös jonkun muun pitää itsesi lisäksi lukea koodia. Kurssin assistenteilta saa apua nopeammin, jos koodisi muuttujanimet ovat selkeitä.
Muuttujien
nimeäminen on myös hyvä litmustesti sille, oletko itse ymmärtänyt koodiasi. Jos nimittäin et osaa antaa jonkin lausekkeen
lopputulokselle sopivaa nimeä, on syytä miettiä oletko ymmärtänyt ylipäätään, mitä kyseinen lauseke tekee. Tällä kurssilla seurataan Pythonin tyylioppaan mukaisia suosituksia, minkä vuoksi nimille ehdotetaan seuraavaa: muuttujien nimet kirjoitetaan kokonaan pienillä kirjaimilla, ja monisanaisissa nimissä yksittäiset sanat erotetaan toisistaan alaviivalla. Siis tähän tapaan:
aasin_korkeus = 100
. Poikkeuksena SI-yksiköt – mikäli käytät niitä, on parempi käyttää oikeaa tunnusta, jolloin se saattaa joskus olla iso kirjain pienen sijaan. SI-yksiköistä tietenkin tällä tavoin voi käyttää vain niitä, joiden symboli löytyy englanninkielisistä aakkosista.Matematiikka laajenee silmissä¶
Tähän asti esitellyillä eväillä syntyy jo koodia, jolla voi ratkaista yksinkertaisia yhtälöitä ja harrastaa vähän muutakin matematiikkaa. Nelilaskinta vastaavaa toiminnallisuus on jo toki jotain, mutta yksinään sillä ei vielä saa paljoa aikaan. On siis laajennettava tajuntaamme.
Osaamistavoitteet: Tämän osion jälkeen tunnet matemaattiset perusoperaattorit. Myös koodirivien kirjoitus alkaa saada uusia piirteitä, kun samalla rivillä tehdään useampia asioita. Opimme myös, että koodilla on suoritusjärjestys, joka vastaa jotakuinkin matematiikkaa. Käsitteet kokonaisluku ja liukuluku tulevat tutuiksi, kuin myös niihin liittyvät komplikaatiot.
Operaatio neliöjuuri¶
Operaattori
on symboli, joka määrittää suoritettavaksi operaation
(esim. matemaattisen operaation, kuten yhteenlasku, jolloin operaattori on +-merkki). Operaatiossa käytetään operandeja
, jotka sijoittuvat operaattorin molemmin puolin, ja ovat siis arvoja joille operaatio suoritetaan. Yleisimpiä ohjelmoinnissa ovat matemaattiset operaattorit, joita käsitellään tässä materiaalissa, sekä loogiset operaattorit joita puolestaan pyöritellään seuraavassa materiaalissa. Python tuntee matemaattiset perusoperaattorit tutuilla merkeillä: +, -, * ja / (yhteen-, vähennys-, kerto- ja jakolasku vastaavasti). Uupumaan jää lähinnä potenssiin korotus sekä juuri. Molemmat tehdään itse asiassa samalla operaattorilla: **. Sinänsä loogista, koska juuren ottaminen vastaa potenssiin korotusta luvulla, joka on pienempi kuin yksi:
kuution_tilavuus = sivun_pituus ** 3
sivun_pituus = kuution_tilavuus ** (1 / 3)
Jakolaskuun liittyy vielä pari
operaattoria
, joille on ajoittain tarvetta. Ensinnäkin //-operaattori suorittaa kokonaislukujakolaskun. %-operaattori puolestaan antaa jakojäännöksen. Tutkitaan näitä lyhyesti tehtävän muodossa ja palataan niihin sitten, kun niille on konkreettisempaa tarvetta (spoiler warning: kolmannessa materiaalissa). Kuten matematiikassa,
operaattoreilla
on myös Pythonissa suoritusjärjestys
. Matemaattisten operaattoreiden osalta järjestys on tuttu: kerto- ja jakolaskut ensin, sitten yhteen- ja vähennyslaskut, aina vasemmalta oikealle. Suoritusjärjestystä voi muuttaa totuttuun tapaan suluilla, jolloin suluissa olevat kokonaisuudet suoritetaan ensin. Suoritusjärjestystä ja lisätietoa operaattoreista voi tutkailla Pythonin dokumentaatiosta. Dokumentaation lukeminen yleisesti ottaen on yksi keskeinen ohjelmointitaito. Kahdenlaisia numeroita¶
Matematiikassa lukuja jaetaan erilaisiin ryhmiin, kuten kokonaislukuihin ja desimaalilukuihin. Tämä jako on myös ohjelmoinnissa tärkeä. Luvut erotetaan toisistaan siten, että desimaaliluvuissa on desimaalipiste. Näinpä siis 5 olisi kokonaisluku, mutta 5.0 olisi desimaaliluku. Suomalaisille tuttu desimaalipilkku ei siis ole käytössä, sillä pilkulla on aivan eri merkitys Pythonissa.
Jos ollaan aivan tarkkoja, tietokoneet eivät käsittele kovinkaan hyvin aitoja desimaalilukuja johtuen siitä, että niiden sielunelämä muodostuu pohjimmiltaan nollista ja ykkösistä. Tästä syystä ei olekaan tapana puhua desimaaliluvuista, vaan
liukuluvuista
(floating point number, float). 87.41871478 # Tässä on liukuluku
Liukuluku on desimaaliluvun approksimaatio. Se ei siis ole täysin tarkka. Tyypillisesti epätarkkuudet ilmenevät niin monen desimaalin jälkeen, että käytännön sovelluksissa liukulukujen tarkkuus on aivan riittävä, koska tuloksia pyöristetään joka tapauksessa. Esimerkiksi kuutiojuuri 64:stä otetaan lausekkeella
64 ** (1 / 3)
, ja tuloksen pitäisi olla tasan 4. Liukulukujen epätarkkuus nostaa kuitenkin tässä päätään, ja tulos on (koneella jolla materiaalia kirjoitettiin) 3.9999999999999996. Asiasta voi lukea tarkemman kuvauksen Pythonin omasta tutoriaalista mikäli yksityiskohdat kiinnostavat. Heitto ei ole hälyttävä ottaen huomioon, että pyöristämällä saadaan 4.0, ja virhekin on niin monen desimaalin takana, että laskutoimituksia saa suorittaa melkoisen määrän ennen kuin lopputulos eroaa tarkasta. On silti yksi tapa saada merkittävä virhe aikaan hyvin nopeasti. Ohjelmoinnissa on ajoittain tarpeellista
muuttaa lukuja
toisen tyyppiseksi. Useammin niin päin, että liukuluvusta
täytyy saada kokonaisluku johonkin sellaiseen tarkoitukseen, mihin liukuluvut eivät kelpaa. Havainnollistavana esimerkkinä otettaakoon kuvaruudun koordinaatit. Kuvaruutu muodostuu fyysisesti pikseleistä, joten se on diskreetti koordinaatisto (verrattuna jatkuvaan koordinaatistoon). Esimerkiksi hiiren kursorin kärki osoittaa aina jotain pikseliä, eikä se voi koskaan olla pikselien välissä. Graafisten objektien sijaintia ruudulla ei siis voi käsitellä desimaalilukuina. Kun siis ohjelmakoodi laskee graafisen objektin sijaintia tai kokoa, sen täytyy muuttaa numeroarvot kokonaisluvuiksi ennen objektin piirtämistä.
Muunnos tapahtuu Pythonissa kahdella
funktiolla
: int
ja float
. Nimensä ne saavat englannin kielen sanoista integer (kokonaisluku) ja floating point number (liukuluku). Palataan kohta siihen, mitä funktiot ovat.In [1]: float(4)
Out[1]: 4.0
In [2]: int(4.0)
Out[2]: 4
Näyttää aika yksinkertaiselta. Mutta?
Äärimmäisen tärkeä fakta int-funktion toiminnasta: se palauttaa luvusta sen kokonaislukuosan. Mitään pyöristystä ei tapahdu. Saman tuloksen saa aikaan suorittamalla kokonaislukujakolaskun ykkösellä eli
3.9999999999999996 // 1
. Jos siis lähdetään muuttamaan liukulukuja
tuottavan laskun lopputulosta suoraan kokonaisluvuksi, voidaan saada kertaheitolla aikaan hyvin suuri virhe. Lopputulos pitäisikin pyöristää ensin jollain tapaa, mihin tarvitsemme uusia työkaluja.Funktiot - uhka vai mahdollisuus?¶
Seuraavaksi esiintymisvuorossa ovat funktiot (engl. function), joita myös joissain yhteyksissä aliohjelmiksi kutsutaan. Terminä funktio voi olla tuttu matematiikasta. Yleisimpänä ilmentymänä merkintä f(x). Merkintä siis määrittelee lausekkeen, jonka lopputulos riippuu x:n arvosta. Tämä on hyvä lähtökohta ohjelmoinnissa esiintyvien funktioiden ymmärtämiseen.
Osaamistavoitteet: Tämä osio tekee funktion käsitteen tutuksi: mitä funktiot ovat, miksi niitä on, missä niitä on ja miten niitä käytetään. Opimme myös, miten funktio on toisaalta erillinen osa ohjelmaa, mutta toisaalta kommunikoi muun ohjelman kanssa. Lisäksi muutama perusfunktio tehdään tutuksi.
Funktioiden syvin olemus¶
Funktiot
ovat kierrätystä kauneimmillaan. Jos on jo olemassa koodinpätkä, joka tekee tietyn asian, on turhaa ryhtyä keksimään pyörää uusiksi. Funktioita löytyy Pythonin sisältä ns. sisäänrakennettuina funktioina
sekä todella lukuisista moduuleista
ja kirjastoista
. Funktioita voi tuottaa myös itse, mikä on äärimmäisen hyvä tapa. Niin hyvä, että se kannattaa omaksua heti ohjelmoinnin opettelun alussa. Alla olevaa esimerkkiä ei tarvitse ymmärtää, mutta se antaa esimakua siitä, mitä on pian odotettavissa, kun pääsemme työstämään omia funktioitamme:def laske_keskiarvo(arvot):
return sum(arvot) / len(arvot)
keskiarvo = laske_keskiarvo((6, 8, 10, 7, 9, 8, 5, 7))
Ideaalisesti
funktio
muodostaa toiminnallisen, eristetyn kokonaisuuden. Toisin sanoen siis funktion toiminta ei ole riippuvainen sen ulkopuolella olevasta koodista. Tämä tarkoittaa, että funktion voi ottaa käyttöön missä tahansa ja odottaa, että se toimii aina samalla tavalla. Niihin kuuluvat myös aiemmin käytetyt int ja float. Vastaavasti ohjelmoijan ei tarvitse tietää mitään funktion sisäisestä toiminnasta vaan hänelle riittää tietää, miten sitä käytetään. Tässä auttaa yleensä funktion dokumentaatio, jonka tehtävä on kertoa kaikki oleellinen funktion toiminnasta. Pythonin sisäänrakennetut funktiot
ovat luonnollisesti juuri tässä kuvatun kaltaisia. Vaikka ideaalinen funktio elääkin eristyksissä, se ei tarkoita, etteikö sillä olisi keinoja viestiä ulkomaailman kanssa. Tämän viestinnän ymmärtäminen on keskeistä funktioiden oppimisessa. Funktio on muun ohjelman näkökulmasta musta laatikko. Sinne menee jotain sisään, ja jotain tulee ulos. Sattumoisin nämä kaksi kanavaa, sisään ja ulos, ovat myös se
rajapinta
, jonka avulla muu koodi kommunikoi funktion kanssa. Funktioiden kohdalla puhutaan
kutsumisesta
(engl. call). Tässä prosessissa on kaksi osapuolta: kutsuja ja kutsuttava. Kutsuttavalla tarkoitetaan funktiota, jota halutaan käyttää. Kutsuja puolestaan on se osa ohjelmaa, joka funktiota tarvitsee. Kutsuttaessa käytetään
funktion
määrittelemää rajapintaa. Tämä määrittää säännöt, millaista tavaraa funktiolle voi antaa, missä järjestyksessä, ja myös sen, mitä sieltä voi odottaa saavansa takaisin. Funktion itsensä kannalta tärkeää on se, että kutsuja noudattaa määrittelyjä siitä, millaisten asioiden antaminen sille on sallittua.Tiedämme toistaiseksi int-funktiosta sen, että sille annetaan lukuarvo, josta se palauttaa sitten takaisin sen kokonaislukuosan. Sovitaan siis, että olemme laskemassa liikkuvan objektin liikettä x-akselilla koordinaatistossa. Tämä tapahtuu kertomalla objektin suuntavektorin x-komponentti (esim. 0.32) objektin nopeudella (esim. 10 yksikköä).
nopeus = 10
suunta_x = 0.32
liike_x = int(suunta_x * nopeus)
Esimerkkiä itsessään ei ole välttämätöntä sisäistää. Kyseessä on vain skenaario, jossa luvun muuttamista kokonaisluvuksi saattaa oikeasti tarvita ja johon törmätään esimerkiksi pelikoodauksessa. Keskeistä on kolmas rivi, jossa esiintyy
funktion
kutsuminen
. Sulkujen sisällä oleva osa on siis se, mitä funktiolle annetaan käsiteltäväksi. Huomion arvoista suoritusjärjestyksestä on se, että lauseke suunta_x * nopeus
suoritetaan ensin, joten funktio saa käsiteltäväksi kertolaskun tuloksen (eli 3.2). Kun funktio sitten suorittaa työnsä, pölyn laskeuduttua liike_x-muuttujaan jää sijoitettavaksi
kokonaislukuarvo 3. Alla oleva animaatio visualisoi funktiokutsun toimintaa.Mahdollisuuksien maailma¶
Päädyimme alunperin ihmettelemään funktioita, koska halusimme muuttaa lausekkeen
64 ** (1 / 3)
tuloksen kokonaisluvuksi oikein. Pelkkä int(64 ** (1 / 3))
antoi hämmentävästi arvon 3. Syy selvisi, mutta ratkaisu ei. Funktioita
on Pythonin mukana lukuisia, kuten voimme todentaa esimerkiksi Pythonin dokumentaatiosta. Kurssin alkupuolella leikimme pitkälti tuolta sivulta löytyvillä funktioilla, emmekä niistäkään läheskään kaikilla. Englannin osaamisesta on yleensä hyötyä ohjelmoinnissa. Tälläkin kertaa auttaa, kun tiedämme, että pyöristämistä tarkoittava sana on round.Seuraavaksi vuorossa on lyhyt opastus siihen, miten funktion kuvauksia luetaan. Otetaan siis malliksi tarkoitukseemme sopiva round.
Ensimmäinen rivi, jolla on
funktion
nimi, on tärkeä. Juuri tämä rivi kertoo millä tavalla funktiota voi kutsua
. Suluissa olevat sanat, number ja ndigits, ovat nimiä funktion vastaanottamille arvoille
, ja nimitykset tyypillisesti pyrkivät jollain tapaa kuvaamaan sitä, millainen annettavan arvon tulee olla. Funktion vastaanottamista arvoista ensimmäinen eli number on tässä tapauksessa pyöristettävä luku ja toinen eli ndigits (lyhenne; number of digits) desimaalien lukumäärä, jonka tarkkuuteen pyöristetään. Näitä nimityksiä funktion vastaanottamille arvoille, number ja ndigits, kutsutaan
parametreiksi
, joka on muuttujan
erikoistapaus. Kun puolestaan käytämme eli kutsumme funktiota ja annamme sille jonkun tietyn pyöristettävän luvun ja tietyn desimaalien lukumäärän (esimerkiksi 3.56 ja 1), kyseisiä arvoja kutsutaan argumenteiksi
. In [1]: round(3.56, 1)
Entä nuo hakasulut ndigits-parametrin ympärillä? Kyseessä on dokumentaatiossa käytetty merkintätapa, jolla ilmaistaan, että kyseisen arvon antaminen on vapaaehtoista. Se ei siis ole funktion määrittelyn
syntaksia
! Jos kurkkaat miten round-funktio on määritelty, se näyttää tällaiselta: def round(number, ndigits=0):
. Merkintä tarkoittaa, että ndigits-muuttujalle annettava arvo voidaan jättää pois funtkiota kutsuttaessa. Teksti myös kertoo, mitä tapahtuu, jos ndigits-parametria vastaava arvo jää kutsuta pois: funktio pyöristää nollan desimaalin tarkkuuteen. Nämä ovat siis identtisiä: In [1]: round(4.451, 0)
Out[1]: 4.0
In [2]: round(4.451)
Out[2]: 4.0
Koska ensimmäisen rivin
, 0
sulkujen sisällä on käytännössä tarpeetonta merkitä, tapana on jättää se kirjoittamatta. Näin menetellään yleensä aina, jos halutaan funktion
käyttäytyvän sen omien oletusarvojen
mukaisesti. Valinnaiset argumentit
annetaan siis vain silloin, kun niiden halutaan poikkeavan oletusarvosta:In [1]: round(4.451, 2)
Out[2]: 4.45
Kun
funktiolle
annetaan useita argumentteja
, ne erotetaan aina toisistaan pilkulla. Tapana on myös, kuten tekstiäkin kirjoittaessa, laittaa yksi välilyönti jokaisen pilkun jälkeen. Argumenttien arvot sijoitetaan parametreihin
siinä järjestyksessä kuin ne on annettu. Tässä esimerkissä siis number-parametri saa arvonsa argumentista 4.451 ja vastaavasti ndigits-parametri saa arvonsa
argumentista 2. Parametri ja argumentti eivät ole sama asia, mutta niillä on sama arvo. Argumentti voi olla literaaliarvo
tai muuttuja
, mutta parametri on aina muuttuja.Funktiot käytössä¶
Nyt voimme viimein kirjoittaa koodinpätkän, jolla lausekkeen
64 ** (1 / 3)
arvo
saadaan kokonaislukuna oikein. tulos = round(64 ** (1 / 3))
tulos = int(tulos)
Sama asia voidaan myös ilmaista yhdellä rivillä, mikä on tässä tapauksessa oikeastaan suotavampaa:
tulos = int(round(64 ** (1 / 3)))
Suoritusjärjestys
seuraa edelleen tuttuja periaatteita: sisempi funktiokutsu
käsitellään ensin. Koska rivillä on jo aika paljon asioita, seuraava animaatio vielä havainnoillistaa suoritusjärjestyksen. Se, kirjoittaako asioita paljon samalle riville vai jakaako niitä
muuttujien
käytöllä usealle, on pitkälti itsestä kiinni. Tärkeintä on luettavuus, mistä syystä on vältettävä liian pitkiä ja monimutkaisia rivejä. Toisaalta vastaavasti on syytä välttää jatkuvaa muuttujiin sijoittelua, koska tämä vaikeuttaa koodista oleellisen löytämistä.Nyt tiedämme siis, mitä
funktiot
ovat, mistä niitä löytyy ja vieläpä senkin miten niitä koodissa käytetään. Seuraavaksi tutustutaan tarkemmin funktioiden käyttöön ja esitellään muutamia kurssin kannalta keskeisiä funktioita. Ensimmäisenä tutkimuskohteena on se, miten
kooditiedostoja
ajattaessa saadaan jotain näkyviin. Tulkissa
toimiessahan erinäisten lausekkeiden
lopputulokset tulostuvat näkyviin, ellei niitä tallenna
muuttujiin
. Tulkki siis suorittaa koko lausekkeen, ja lopulta näyttää sen lopputuloksen. Kooditiedostot puolestaan eivät ajettaessa näytä lausekkeiden tuloksia automaattisesti, vaan käytännössä heittävät ne roskiin mikäli niitä ei sijoiteta muuttujiin. Niinpä esimerkiksi kooditiedostossa rivi 4451 + 1337
ei käytännössä tee yhtään mitään. Tuottaahan se tuloksen (5788), mutta sillä ei tehdä mitään, joten se katoaa välittömästi bittiavaruuden syövereihin. Yleensä ohjelmien suorituksesta olisi kuitenkin hyödyllistä nähdä lopputuloksia, joten tarvitaan keino saada niitä näkyviin. Avuksi hätään rientää
print
-funktio. Otetaan yksinkertainen käyttöesimerkki: tulos = int(round(64 ** (1 / 3)))
print(tulos)
tai ilman muuttujaan sijoitusta välissä:
print(int(round(64 ** (1 / 3))))
Yksinkertaisimmillaan print-funktio on
funktio
, joka tulostaa sille annetun argumentin
, ja rivinvaihdon perään. Jos koodissa on siis monta print-funktiokutsua, jokainen niistä tulostaa saamansa argumentin eri riville.Näin alussa, kun käsittelemme pelkkiä numeroita, muut käyttökelpoiset funktiot print-funktion ohella ovat lähinnä matemaattisia. Näitä ovat muun muassa minimin, maksimin sekä itseisarvon ottaminen:
min
, max
ja abs
. Pythonin mukana tulee enemmänkin matemaattisia funktioita, mutta ne on niputettu omaan moduuliinsa
. Niihinkin tustustutaan lähitulevaisuudessa.Python-konsolissa
työskennellessä yksi kätevä funktio on help
. Ilman mitään argumentteja se avaa interaktiivisen aputoiminnon. Jos sille taas antaa argumentiksi
funktion nimen, saa tietoja funktion toiminnasta. Seuraava kutsu tulostaisi tietoa round-funktiosta:help(round)
Tyypillisesti nämä help-funktion tulosteet ovat lyhyempiä kuin dokumenttisivulla olevat ja sisältävät vähemmän yksityiskohtia. Käyttämällä help-funktiota voi kuitenkin nopeasti muun muassa katsoa, mitkä argumentit funktiolle pitää tai voi antaa. Ohjeista pääsee pois painamalla Q.
Funktioiden salatut elämät¶
Funktioita
osataan nyt alustavasti käyttää. Seuraavaksi on edessä matka kuun pimeälle puolelle, missä funktioita tehdään itse. Samalla käydään läpi kooditiedoston
rakennetta. Osaamistavoitteet: Tämän osion jälkeen tunnet funktion määrittelyyn liittyvän syntaksin ja tiedät, miten funktion sisällä oleva koodi eroaa muusta koodista. Lisäksi muuttujien ja niiden arvojen käyttäytyminen funktiota kirjoittaessa ja kutsuttaessa selkenevät. Lisäbonuksena opit, miten kooditiedosto kannattaa järjestää ja miksi.
Funktion rakennuspalikat¶
Funktion
sisältö itsessään on aivan tavallista koodia. Funktioiden kirjoittamiseen liittyy kuitenkin kaksi erityistä avainsanaa
joita käytetään vain funktioissa. Näistä ensimmäinen on def
, jolla aloitetaan funktion määrittely. Toinen puolestaan on return
, joka määrää sen, mistä kohdasta funktiokoodia palataan takaisin funktiota kutsuneelle
riville ja mikä arvo
sinne palautetaan
. Funktiokoodi erotetaan muusta koodista sisentämällä def-lauseen jälkeen. Otetaan havainnollistava esimerkki avaamaan näitä käsitteitä: def laske_matka(nopeus, aika):
return nopeus * aika
Tämä funktio on hyvin yksinkertainen - niin yksinkertainen että sellaista ei ehkä kannata tehdä oikeassa koodissa. Sen ensimmäinen rivi kertoo Pythonille
avainsanalla
def (lyhennös sanasta define, määrittää), että tästä kohtaa alkaa funktion määrittely. Seuraava rivi on sisennetty
, mikä kertoo, että sille kirjoitettu koodi sijaitsee funktion sisällä, eristyksissä muusta koodista. Puretaan kuitenkin def-riviä lisää. Siihen kuuluu kolme merkittävää komponenttia: itse def-sana, funktion nimi ja funktion [!term=Parameter!parametrit[!term!]. Funktioiden
nimeämisessä on samat säännöt ja pitkälti samat suositukset kuin muuttujien
nimissä. Funktioiden nimille on kuitenkin tyypillistä, että ne niissä on verbi. Tämä korostaa sitä, että funktio tekee jotain. Toinen hyvin tyypillinen tapa erottaa, milloin kyseessä on muuttuja ja milloin funktio, on se, että sekä määritellessä että kutsuttaessa funktion nimeä seuraavat välittömästi sulut.matka = laske_matka(100, 2) # matka on muuttuja, laske_matka puolestaan funktio
Funktion määrittelyrivillä olevat sulut sisältävät funktion parametrien nimet, tässä tapauksessa siis nopeus ja aika, pilkulla erotettuna. Määrittelyrivi päättyy aina kaksoispisteeseen. Kaksoispisteellä on Pythonissa syntaktinen merkitys: se aloittaa uuden
koodilohkon
, eli sitä seuraava rivi tulee olla sisennetty
. Funktion määrittely päättyy kun sisennetty koodilohko päättyy. Lopuksi funktioon kuuluu yleensä rivi, joka alkaa avainsanalla
return
. Tämä rivi merkitsee kohtaa, jossa funktion suorittaminen päättyy. Lisäksi se kertoo, mitä funktio palauttaa
lopputuloksenaan. Se siis määrittelee funktion toisen liittymäkohdan muuhun ohjelmaan siinä, missä funktion määrittelyrivillä olevat parametrit
määrittävät ensimmäisen. Parametristä argumentaatiota¶
Funktioiden kanssa pelatessa keskeiset käsitteet ovat
parametri
ja argumentit
. Parametri on paikallinen muuttuja
- nimi, jolla argumenttina saatua arvoa kutsutaan funktion sisällä. Argumentti puolestaan on literaaliarvo
tai muuttuja
, jonka arvon parametri saa kun funktiota kutsutaan
. Argumentti siis sijoitetaan
parametriin.Argumenttien
ja parametrien
järjestys määrää sen, mihin parametriin kunkin argumentin arvo
sijoitetaan. Huomioitavaa on, ettei arvokaan varsinaisesti siirry mihinkään. Funktion sisällä näihin arvoihin aletaan vain viitata
käyttämällä parametrien nimiä. Argumenttien ja parametrien käytöstä valaisee seuraava animaatio.Tärkeä konsepti parametrien ymmärtämisessä on muuttujien paikallisuus.
Paikalliset muuttujat
eivät näy oman kontekstinsa - tässä tapauksessa funktion - ulkopuolelle. Muun ohjelman näkökulmasta ne eivät ole olemassa. Funktion sisällä paikallisia nimiä ovat kaikki funktion parametrit
sekä muuttujat, jotka luodaan funktion sisällä käyttämällä sijoitusta
. Paikalliset muuttujat lakkaavat olemasta heti kun funktion suoritus päättyy.Kahden eri funktion sisällä voi olla saman nimisiä muuttujia eivätkä ne millään tapaa sotke toistensa toimintaa. Pääohjelman muuttujat sen sijaan
näkyvät kaikkialle
, mutta niihin ei voi kirjoittaa muualla kuin pääohjelmassa. Jos funktion sisällä sijoitetaan muuttujaan, jonka nimi on jo pääohjelmassa käytössä, luodaan itse asiassa vain saman niminen paikallinen muuttuja. Pääohjelmaksi luetaan kaikki tässä vaiheessa mikä on kiinni koodin vasemmassa reunassa.Paluumatkan vuoro¶
Siinä missä
funktiokutsussa
funktion sisälle välitetään arvoja
argumenteista
parametreihin
, vastaavasti funktiosta saadaan arvoja takaisin paluuarvon
kautta. Paluuarvo määritellään return
-avainsanalla
alkavalla rivillä, kuten ylläolevassa tehtävässä. Tässäkin on oleellista pitää mielessä muuttujan
ja arvon
ero: mitään funktion sisällä olevaa muuttujaa ei vapauteta ulkomaailman käsiteltäväksi. Paluun yhteydessä määritetään ainoastaan arvo, jota funktion kutsuja voi käyttää haluamallaan tavalla. Aiempi esimerkki hieman muunneltuna:def laske_matka(nopeus, aika):
matka = nopeus * aika
return matka
v = 100
t = 0.6
s = laske_matka(v, t)
Koodissa
funktio
palauttaa
viittauksen
matka-muuttujan sisältämään arvoon
(ts. siihen arvoon, johon matka-muuttuja viittaa). Viimeisellä rivillä funktion ulkopuolella tälle viittaukselle annetaan nimi s. Siis nimi matka ei missään vaiheessa näy funktiosta ulos, ja itse asiassa koko muuttuja lakkaa olemastafunktion suorittamisen jälkeen. Sen sijaan viimeisen rivin suorituksen jälkeen on olemassa muuttuja s, joka sisältää saman arvon kuin matka-muuttuja sisälsi funktion suorituksen päättyessä. Arvo siis siirtyy funktiosta ulos, mutta muuttuja ei.Jos edelleen pidetään mielessä, että funktiokutsu on suorituksen jälkeen pelkkä paluuarvo, funktiota kutsuvalla rivillä voidaan
sijoituksen
sijaan myös luovuttaa yhdeltä funktiolta saatu paluuarvo eteenpäin toiselle funktiolle argumenttina
, kuten eräässä aiemmassa esimerkissä tehtiin: tulos = int(round(64 ** (1 / 3)))
Funktio voi myös palauttaa useita arvoja. Yksi tällainen funktio on divmod, joka suorittaa kokonaislukujakolaskun ja palauttaa sekä osamäärän että jakojäännöksen. Funktion palauttessa useita arvoja ne voidaan sijoittaa useaan muuttujaan kirjoittamalla oikea määrä muuttujia pilkulla erotettuna
sijoitusoperaattorin
vasemmalle puolelle:In [1]: jaettava = 10
In [2]: jakaja = 3
In [3]: osamaara, jakojaannos = divmod(jaettava, jakaja)
In [4]: print(osamaara)
3
In [5]: print(jakojaannos)
1
Sama temppu nähtiin jo hetki sitten tehtäväesimerkissä kun palautettiin 2-ulotteisen vektorin komponentit funktiosta. Koska sijoituksen oikealla puolen on sijoitusvaiheessa oikeastaan vain kaksi lukuarvoa, loogisesti pitäisi olla myös mahdollista sijoittaa suoraan kahteen muuttujaan kaksi
literaaliarvoa
, ja onkin:In [1]: jaettava, jakaja = 10, 3
Funktioiden kirjoittamisesta¶
Funktiot
vaikuttavat sisältävän valtavasti ymmärrettävää asiaa. Mikä niistä tekee vaivan arvoisia? - Funktiot auttavat pitämään koodin helposti käsiteltävän kokoisina paloina.
- Funktioilla voi antaa kokonaisuuksille nimiä, jotka kertovat selkeämmin kuin koodi siitä, miten ohjelman suoritus kulkee.
- Samaa koodia ei tarvitse kirjoittaa tai kopioida moneen kertaan.
Toivottavasti tämä listauksen seurauksena koet funktioiden hyödyistä vastaavanlaisen ahaa-elämyksen kuin alla.
Ideaali funktioiden rajaamisessa on se, että funktio tekee yhden asian. Funktion nimestä tulisi myös selkeästi ilmetä mikä tämä yksi asia on. Jos nämä tavoitteet eivät täyty, joko funktio tekee liian paljon asioita tai se on huonosti nimetty.
Toinen ideaali funktioiden kanssa on se, että ne ovat mahdollisimman uusiokäytettäviä. Jos siis ohjelmassa tehdään useassa kohtaa samankaltaisia asioita, ideaalitapauksessa ne kaikki voidaan hoitaa samalla funktiolla. Funktion uusiokäytettävyyteen vaikuttaa erityisesti
parametrien
valinta. Laskutoimituksissa kaikki kaavaan liittyvät tuntemattomat tekijät kannattaakin käsitellä funktion argumentteina
, kuten laske_matka-esimerkissämme. Parametrisaatio
on kyky joka kehittyy ohjelmointikokemuksen myötä. Sitä voi kuitenkin harjoittaa katsomalla samanlaisia ohjelmarakenteita ja tunnistamalla niistä eroavuudet. Usein tämän eroavuuden voi korvata muuttujalla, joka laitetaan funktion parametriksi. Kurssin harjoitustehtäville tyypillistä on se, että niissä tehdään varsin uusiokäytettäviä funktioita. Kannattaa siis miettiä haroitustehtävien kohdalla miksi funktiot on määritelty siinä siten kuin ne on.Funktioiden sijoittelusta¶
Funktioiden
sijainnilla koodissa on merkitystä. Funktio luodaan sillä hetkellä, kun sen määrittelyrivi (merkitty avainsanalla def
suoritetaan. Funktion määrittelyrivin tulee siis olla ennen koodia, joka funktiota käyttää. Tärkeää on myös muistaa, että funktion sisällä oleva koodi suoritetaan vasta kun funktiota kutsutaan
. Funktioiden keskinäisellä järjestyksellä ei ole siten merkitystä. Funktion (A) sisällä olevassa koodissa voidaan siis kutsua myös sellaista funktiota (B), joka määritetään koodissa myöhemmin kunhan funktiota A ei kutsuta ennen funktion B määrittelyä.Funktioiden ryhmittelyyn kannattaa valita looginen tapa, joka miellyttää itseä eniten. Yleisesti ottaen funktioiden määrittelylle tulisi olla koodissa oma alueensa, jossa ei tehdä muuta kuin funktioiden määrittelyä. Kooditiedosto voidaankin jakaa kolmeen osioon:
- alustusosio, jossa tehdään erinäisiä valmisteluja
- funktio-osio, jossa määritellään funktiot
- pääohjelmaosio.
Alustusosiossa tehdään asioita, joilla on merkitystä koko tiedoston kannalta. Tähän kuuluu lähinnä
vakioarvojen
määrittely ja moduulien
käyttöönotto, johon perehdymme myöhemmin. Vakioista puhutaan lyhyesti seuraavassa osiossa. Funktio-osiossa määritellään funktiot
: sinne siis kirjoitetaan def-lauseita
ja niitä seuraavia sisennettyjä koodirivejä, jotka määrittävät funktion toiminnan. Pääohjelmaosio on se, mistä ohjelman suoritus varsinaisesti alkaa. Sitä ennen kaikki funktiot pitäisi olla määritelty.
Pääohjelman
tulisikin lähinnä koostua muiden funktioiden kutsumisesta
, jotta sen kulku on mahdollisimman selkeä. Joissain ohjelmointikielissä "pääohjelma" sijoitetaan erityiseen main-funktioon, ja ohjelman suoritus alkaa tästä funktiosta.Kyselytunti¶
Oppimistavoitteet: Tämä osio tarjoaa eväät yksinkertaisten tulosteiden tekemiseen sekä perusteet kysymyksien esittämiseen käyttäjälle. Keskeinen ongelma on, miten käyttäjän näppäimistöltä saadaan numeroarvo ohjelman käsiteltäväksi.
Lisää vektorigeometriaa¶
Lähdetään liikkeelle aiemmasta esimerkistä. Aiemmassa tehtävässä esiteltiin koodinpätkä, jolla laskettiin suuntavektoreita sekä liikettä suuntavektorin ja nopeuden perusteella. Esimerkistä löytyy yksi ilmeinen vika helposti: jotta ohjelma toimisi kunnollisena vektorigeometrialaskimena, sille olisi varmaan hyvä saada syötettyä pisteitä muutenkin kuin muokkaamalla
kooditiedostossa
olevien muuttujien
arvoja. Toiseksi ohjelma ei tällä hetkellä tulosta muuta kuin numeroita ilman mitään selitystä mitä ne ovat.4.0 3.0
Käyttäjällä ei ole mitään käsitystä siitä, mistä nämä luvut tulevat tai mitä ne ovat. Ohjelmassa on siis ainakin kaksi selkeää parannuskohdetta: vektorin alku- ja loppupisteet pitäisi kysyä käyttäjältä, ja ohjelman pitäisi antaa ulos tietoa siitä, mitä sen tulosteet tarkoittavat.
Otetaan toistaiseksi koodista käyttöön pelkästään suuntavektorin laskeminen - sijainnin laskentafunktion voi kuitenkin jättää sinne myöhempää käyttöä varten. Kooditiedosto löytyy alta, jos olet sattunut sen unohtamaan. Siitä on myös poistettu rivit, jotka eivät liity suuntavektorin laskemiseen, ja lisätty suuntavektorin tulostus.
Se puhuu!¶
Aloitetaan ohjelman suoritus siitä, että kerrotaan käyttäjälle seuraavaa: "Tämä ohjelma laskee kahden pisteen välisen suuntavektorin 2-ulotteisella tasolla". Tämä vaatii jotain uutta eli kyvyn käsitellä tekstiä. Tekstin ohjelmointikielinen termi on
(•_•)
( •_•)>⌐■-■
(⌐■_■)
...jonossa. Englanniksi merkkijono on string ja lyhennetään yleensä str.
merkkijono
, mikä viittaa siihen, että merkkejä on siinä kiltisti peräkkäin. Voisi jopa sanoa, että siinä on merkkejä... (•_•)
( •_•)>⌐■-■
(⌐■_■)
...jonossa. Englanniksi merkkijono on string ja lyhennetään yleensä str.
Toistaiseksi kirjaimia on käytetty asioiden kuten muuttujien ja funktioiden nimeämiseen. Miten sitten Python erottaa, milloin on kyse nimestä, ja milloin merkkijonosta? Lainausmerkki on hyvin yksiselitteinen ohje Python-tulkille, että tästä kohtaa alkaa merkkijono. Merkkijono myös jatkuu seuraavaan lainausmerkkiin asti. Yksinkertaiset (
'
) ja kaksinkertaiset ("
) lainausmerkit ovat molemmat käypiä kunhan merkkijono alkaa ja loppuu samalla lainausmerkillä. Lyhyesti koodilla esitettynä: "merkkijono"
'toinen merkkijono'
muuttuja
Lainausmerkkien valintaan ei ole varsinaisesti mitään muuta sääntöä kuin "valitse yksi". Eli kumpaa tahansa käytätkin, pysy siinä. Me käytämme tässä materiaalissa "-merkkiä, koska se on muissa ohjelmointikielissä yleisempi. Itse asiassa joissain kielissä, kuten C, yksinkertaisilla lainausmerkeillä voidaan merkitä vain yksittäinen merkki ja merkkijono vaatii kaksinkertaiset lainausmerkit.
Merkkijono
on tietotyyppi
, eli se kuuluu samaan joukkoon kokonaislukujen ja liukulukujen
kanssa. Merkkijonot ovat siis arvoja
, ja kaikki, mitä aiemmin todettiin arvoista, pätee ihan samalla tavalla merkkijonoihin kuin numeroihinkin (merkkijonoja tosin ei tietenkään voi esim. pyöristää). Merkkijonoja voi siis sijoittaa
muuttujiin
, ja voipa niille tehdä myös joitain operaatioita
. Niitä voi myös tulostaa print-funktiolla
ihan samalla tavalla kuin numeroita. Siispä aiemmin ehdotettu tuloste "Tämä ohjelma laskee kahden pisteen välisen suuntavektorin 2-ulotteisella tasolla" saadaan ulos kun se annetaan print-funktiolle argumentiksi
: print("Tämä ohjelma laskee kahden pisteen välisen suuntavektorin 2-ulotteisella tasolla")
Vektoriohjelmamme olisi hyvä kertoa, mitä vektorin komponentteja tulostettavat numerot ovat. Käyttäjälle pitäisi myös informoida, minkä pisteiden välisestä suuntavektorista on kyse, koska tällä hetkellä sitä ei näe mistään. Lisätään sopivat tulostukset
pääohjelmaan
:print("Tämä ohjelma laskee kahden pisteen välisen suuntavektorin 2-ulotteisella tasolla")
alku_x = 2
alku_y = 6
kohde_x = 10
kohde_y = 12
vektori_x, vektori_y = laske_suuntavektori(alku_x, alku_y, kohde_x, kohde_y)
print("Aloituspiste (x y):", alku_x, alku_y)
print("Päätepiste (x y):", kohde_x, kohde_y)
print("Suuntavektori (x y):", vektori_x, vektori_y)
Tähän esimerkkiin on myös ujutettu uusi tapa käyttää print-fuktiota: sille voidaan itse asiassa antaa enemmän kuin yksi tulostettava argumentti. Tarkalleen ottaen print on funktio, joka tulostaa kaikki argumenttinsa - joiden määrää ei ole mitenkään rajoitettu - välilyönnillä erotettuna yhdelle riville ja rivinvaihdoin loppuun (itse asiassa myös sekä erottimen että lopetusmerkinkin voi vaihtaa, mutta ei mennä sinne nyt). Jos ajamme tämän muokatun ohjelman, saamme jo paljon informatiivisemman tulosteen:
Tämä ohjelma laskee kahden pisteen välisen suuntavektorin 2-ulotteisella tasolla Aloituspiste (x y): 2 6 Päätepiste (x y): 10 12 Suuntavektori (x y): 0.8 0.6
Syöttäminen 101¶
Seuraava askel on muuttaa ohjelmaa siten, että alku- ja päätepisteiden toteamisen sijaan se kysyy niitä käyttäjältä. Tällä kurssilla kommunikoimme käyttäjän kanssa pääasiassa
tekstisyötteiden
avulla, koska niiden käyttöön riittää yksi funktio
: input
. Ensimmäinen tehtävä, kun törmätään uuteen funktioon, on tietenkin katsoa, mitä Python itse siitä sanoo: In [1]: help(input)
Help on built-in function input in module builtins:
input(...)
input([prompt]) -> string
Read a string from standard input. The trailing newline is stripped.
If the user hits EOF (Unix: Ctl-D, Windows: Ctl-Z+Return), raise EOFError.
On Unix, GNU readline is used if enabled. The prompt string, if given,
is printed without a trailing newline before reading.
Perusasiat bongataan nopeasti: funktiolla on yksi
valinnainen
argumentti
ja se palauttaa merkkijonon
. Funktion
help-teksti kertoo mm. että käyttäjän päättäessä tekstin syöttämisen
Enter-näppäimen painalluksella, syntyvä rivinvaihto jätetään funktion palauttamasta
merkkijonosta pois. Mikäpä sitten on tuon prompt-parametrin
rooli? Se merkitsee kysymystä, joka näytetään käyttäjälle. Kysymyksellä on tarkoitus on ohjeistaa, mitä käyttäjän pitäisi syöttää.Koska input toimii kuten mikä tahansa muukin
funktio
, sen palauttama
arvo
voidaan tallentaa
muuttujaan
. Ehkä hieman erikoista aiempaan nähden tässä on se, että palautettu arvo ei varsinaisesti mitenkään liity funktiolle annettuun argumenttiin
. Argumentti toimii vain ohjeena käyttäjälle, ja funktio palauttaa arvon, jonka käyttäjä sille tämän ohjeen perusteella antoi. Alla oleva animaatio esittää miten input-funktiokutsu
toimii.Vektorikoodin
pääohjelma
alkaa tällä hetkellä seuraavasti (alun print jätetty pois): alku_x = 2
alku_y = 6
kohde_x = 10
kohde_y = 12
vektori_x, vektori_y = laske_suuntavektori(alku_x, alku_y, kohde_x, kohde_y)
Ensimmäistä neljää riviä muuttamalla saadaan pisteitä kuvaavien
muuttujien
arvot luettua input-funktiolla
käyttäjältä, joten tehdään niin. Samalla voidaan kaukaa viisaasti poistaa rivit, joilla nämä pisteet tulostetaan. Tämä informaatio näkyy nimittäin käyttäjälle nyt siinä vaiheessa, kun häneltä sitä kysytään. Muutetaan siis pääohjelma
seuraavan näköiseksi:print("Tämä ohjelma laskee kahden pisteen välisen suuntavektorin 2-ulotteisella tasolla")
alku_x = input("Anna alkupisteen x-koordinaatti: ")
alku_y = input("Anna alkupisteen y-koordinaatti: ")
kohde_x = input("Anna päätepisteen x-koordinaatti: ")
kohde_y = input("Anna päätepisteen y-koordinaatti: ")
vektori_x, vektori_y = laske_suuntavektori(alku_x, alku_y, kohde_x, kohde_y)
print("Suuntavektori (x y):", vektori_x, vektori_y)
Lisäsimme yhden välilyönnin input-funktion
argumentin
loppuun. Tämä vähentää klaustrofobisia tunteita, kun kysymys ja syötekenttä eivät ole aivan kiinni toisissaan: Anna alkupisteen x-koordinaatti: 2
Ei ole vääriä kysymyksiä, on vain vääriä vastauksia¶
Edellisessä tehtävässä aikaansaadun tekstitulvan pitäisi näyttää jotakuinkin tältä:
Tämä ohjelma laskee kahden pisteen välisen suuntavektorin 2-ulotteisella tasolla Anna alkupisteen x-koordinaatti: 2 Anna alkupisteen y-koordinaatti: 6 Anna päätepisteen x-koordinaatti: 10 Anna päätepisteen y-koordinaatti: 12 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) /joku/kansio/suuntavektori.py in() 15 kohde_x = input("Anna päätepisteen x-koordinaatti: ") 16 kohde_y = input("Anna päätepisteen y-koordinaatti: ") ---> 17 vektori_x, vektori_y = laske_suuntavektori(alku_x, alku_y, kohde_x, kohde_y) 18 print("Suuntavektori (x y):", suunta_x, suunta_y) /joku/kansio/suuntavektori.py in laske_suuntavektori(x0, y0, x1, y1) 1 def laske_suuntavektori(x0, y0, x1, y1): ----> 2 pituus = ((x1 - x0) ** 2 + (y1 - y0) ** 2) ** 0.5 3 ux = (x1 - x0) / pituus 4 uy = (y1 - y0) / pituus 5 return ux, uy TypeError: unsupported operand type(s) for -: 'str' and 'str'
Virheviesti
koostuu kahdesta osasta: pidempi, sisennyksiä sisältävä osa kertoo missä päin ohjelmaa virhe tapahtui. Viimeinen rivi puolestaan kuvaa millaisesta virheestä on kyse. Ensimmäisessä osassa luetellaan traceback
, eli virhetilanteeseen johtaneista funktiokutsuista
kertynyt pino, joka alkaa pääohjelmasta
ja loppuu aina siihen kohtaan koodissa, jossa virhe varsinaisesti tapahtui. Lähtöpisteenä on siis pääohjelman (viestissä <module>) rivi 17, jolla kutsutaan laske_suuntavektori
-funktiota
.Varsinainen virhe kohdataan laske_suuntavektori-funktion sisällä rivillä 2. Viestin viimeinen rivi puolestaan kertoo, että kyseessä on TypeError-niminen
poikkeus
. Tämä nimi viittaa siihen, että koodissa esiintyy vääräntyyppinen
arvo
. Virheen kuvaus kertoo vielä, että rivillä yritetään vähentää kaksi merkkijonoa
toisistaan.Koska
virheiden jäljitys
on ehdottomasti yksi tärkeimmistä ohjelmointitaidoista, käydään virheviesti vielä kertaalleen rivi riviltä läpi:Traceback (most recent call last):
– virheviesti alkaa; kyseessä on virheeseen johtaneista funktiokutsuista koostuvan pinon purkamisesta ja esittämisestä ruudulla (traceback); viimeisimpänä kutsuttu funktio on lueteltu viimeisenä (most recent call last)./joku/kansio/suuntavektori.py in <module>()
– pinon ensimmäinen kohta löytyy kooditiedostosta nimeltä "suuntavektori.py".---> 17 vektori_x, vektori_y = laske_suuntavektori(alku_x, alku_y, kohde_x, kohde_y)
– pinon ensimmäisen kohdan funktiokutsu löytyy tältä riviltä; varsinainen funktiokutsu on laske_suuntavektori(alku_x, alku_y, kohde_x, kohde_y). Myös ympäröivät rivit ovat näkyvissä - nuoli osoittaa virheen aiheuttajan./joku/kansio/suuntavektori.py in laske_suuntavektori(x0, y0, x1, y1)
– pinon toinen (ja tässä virheviestissä viimeinen) kohta löytyy kooditiedostosta "suuntavektori.py" laske_suuntavektori-funktion sisältä – eli sen funktion, jota pinon edellisessä kohdassa kutsuttiin!----> 2 pituus = ((x1 - x0) ** 2 + (y1 - y0) ** 2) ** 0.5
– pinon toisen (ja esimerkin viimeisen) kohdan rivi, eli rivi, jossa varsinainen virhe tapahtui.TypeError: unsupported operand type(s) for -: 'str' and 'str'
– tapahtunut virhe, eli rivillä on käytetty vääräntyyppistä arvoa (TypeError); tarkempi kuvaus siitä, minkälainen kyseinen virhe oli, eli että vähennyslasku ei ole mahdollinen operaatio kahden merkkijonon välillä.
Tärkeää on tosin huomata, että vaikka virhe tapahtuu tässä kohdassa, sen syy ei välttämättä ole tällä rivillä! Niin kuin ei nytkään.
Vektorikoodimme ongelmana siis on, että input-
funktio
tuottaa meille merkkijonon
, ja tarvitsemme kokonaisluvun tai liukuluvun
. Sattumoisin aiemmin tässä materiaalissa oppimamme int- ja float-funktiot osaavat numeroiden lisäksi muuttaa myös sopivanlaisia merkkijonoja numeroiksi. Vaatimuksena tietenkin on, että merkkijonon sisältö näyttää oikeantyyppiseltä: siis joko kokonaisluvulta tai liukuluvulta. Tässä tapauksessa liukuluku on parempi, koska pisteiden ei ole missään vaiheessa sanottu olevan kokonaislukuja. Muunnos merkkijonosta liukuluvuksi voidaan tehdä vaikkapa input-riviä muokkaamalla: alku_x = float(input("Anna alkupisteen x-koordinaatti: "))
Tämän jälkeen ohjelma toimii, jos siihen
syöttää
numeroita. Ihan omanlaisensa suo syntyy siitä, jos käyttäjä ei syötäkään numeroa. Tämän materiaalin puitteissa sovitaan kuitenkin, että eihän kukaan nyt tuommoista virhettä tee... Päivitetty kooditiedosto löytyy alta:Moduulit¶
Osaamistavoitteet: Tämän osion jälkeen tiedät mitä ovat moduulit ja miten käytetään Pythonin mukana tulevia moduuleja. Tarkemmat yksityiskohdat jätetään myöhemmäksi.
Illan tähtivieraina moduulit¶
Tässä osiossa käsitellään Pythonin mukana tulevia
moduuleja
. On paljon muitakin moduuleja - mm. kaikki kirjoittamasi kooditiedostot
- mutta niihin ei ole tarpeen paneutua tässä vaiheessa kurssia. Olet jo käyttänytkin yhtä Pythonin moduulia: esitehtäväsivun piirtelytehtävät käyttivät Turtle-nimistä palikkaa. Käyttöönotto hoidettiin maagisella rivillä, jota ei sen enempää selitelty: from turtle import *
. Mystiikka ei ole hyväksi ohjelmoinnin oppimiselle, joten pureudutaan siihen mitä tässä tapahtuu. Meidän näkökulmastamme Turtle on kokoelma funktioita
- ihan samanlaisia funktioita kuin ne joita tässä materiaalissa on käsitelty. Nämä funktiot siis tulevat Pythonin mukana, mutta ne on paketoitu erilliseen moduuliin. Useimmissa ohjelmissa kun ei tarvita Turtlen kaltaista piirtelylelua, joten sen kymmenet funktiot olisivat turhaa painolastia. Koska moduuleihin paketoidut funktiot eivät ole suoraan käytössä kaikissa Python-ohjelmissa, tarvitaan jokin keino ilmaista Pythonille milloin haluamme niitä käyttää. Tätä tarkoitusta palvelee
import-lause
. Esitehtävissä käytetty import-lause on itse asiassa harvinaisempi tapa käyttää moduuleja. Nyt esitellään se tavallisempi tapa, joka saattaa tässä vaiheessa tuntua hölmömmältä, mutta pitää koodin huomattavasti selkeämpänä projektien laajetessa. Jotta sitä voidaan kunnolla käsitellä, täytyy tietää hieman nimiavaruuksista. Nimiä avaruudessa¶
Kuten tässä materiaalissa on koitettu teroittaa, funktion sisällä määritellyt nimet ovat olemassa vain funktion sisällä. Moduuleihin pätee samanlainen periaate, eli moduulin määrittelemät nimet ovat lähtökohtaisesti käytössä vain moduulin sisällä - näitä nimiä kutsutaan moduulin
nimiavaruudeksi
. Toisin kuin funktioiden kohdalla, moduulien tapauksessa on kuitenkin mahdollista päästä käsiksi näihin nimiin käyttämällä import-lausetta. Yksinkertaisimmillaan import-lause kirjoitetaan import moduulin_nimi
, eli esimerkiksi Turtlen kohdalla import turtle
. Ero aiemmin käytettyyn tapaan on se, että turtle-moduulista ei oteta käyttöön kaikkia nimiä kuten esitehtävissä. Sen sijaan käyttöön tulee vain yksi nimi: turtle
. Miten funktioihin sitten pääsee käsiksi, jos käytössä on pelkkä
turtle
? Muut nimet ovat turtle-moduulin ominaisuuksia, ja Pythonissa tapa jolla näihin päästään kiinni on liittää ominaisuus sen omistajan perään pisteellä erotettuna. Näin esim viivan piirto eteenpäin olisi: turtle.forward(50)
. Tämä tapa vaatii enemmän kirjoittamista, mutta siinä on selkeä etu: yhdellä vilkaisulla nähdään, että forward on nimenomaan turtle-moduuliin kuuluva funktio. Jos taas on tehty kuin esitehtävissä, kaikki nimet on kaadettu samaan laariin ja koodin lukijan pitää selvittää pidemmän kaavan kautta mitkä funktiot on määritelty auki olevassa kooditiedostossa ja mitkä on tuotu muualta. Ympyrän piirto käyttämällä
import turtle
tehtäisiin siis:In [1]: import turtle
In [2]: turtle.circle(30)
Tällä kurssilla esimerkit ja tehtävät käyttävät suomenkielisiä nimiä funktioille yms. joten
nimikonfliktit
ovat harvinaisempia. Jos sen sijaan puhutaan ns. tosielämän ohjelmista, ne ovat itsessään todella laajoja ja käyttävät lukuisia moduuleja. Jos kaikkien moduulien nimet kaadettaisiin samaan laariin, todennäköisyys nimikonflikteille kasvaa merkittävästi. Tyypillisesti tästä seuraa se, että ohjelma ei käynnisty, koska jokin muuttuja on uudelleenmääritelty täysin odottamattomaan arvoon, joka on usein vieläpä eri tyyppiä. On myös tilanteita, missä käy työlääksi kirjoittaa koko ajan moduulin nimi funktion nimen eteen, koska samaa funktiota käytetään jatkuvasti ohjelmassa. Tähän on kuitenkin olemassa parempi keino kuin esitehtävien
from turtle import *
. Tuossa rivissä nimittäin * on lyhenne sille, että otetaan kaikki nimet (ns. wildcard) - sen tilalla voidaan myös listata mitkä nimet halutaan tuoda. Tämä tehdään pilkulla erotettuna. Näinpä siis jos halutaan vaikka pelkästään neliön piirtämiseen tarvittavat funktiot turtlesta, ne voitaisiin ottaa käyttöön myös näin:from turtle import begin_fill, color, done, end_fill, forward, left
Tämän tavan etuna on se, että yhdeltä riviltä voidaan helposti lukea mitkä koodissa käytetyt funktiot on tuotu Turtlesta. Moduulille voidaan myös antaa uusi nimi importin yhteydessä, mikä on käytännöllistä erityisesti pitkien ja usein käytettävien moduulien nimien kanssa. Tällöin rivi kirjoitetaan seuraavanlaiseen muotoon:
import turtle as t
, jossa siis as-operaattorin oikealla puolella on uusi nimi. Ympyrän piirto menisi nyt näin:In [1]: import turtle as t
In [2]: t.circle(30)
Matikkaa moduulista¶
Kuten todettua, tässä vaiheessa kurssia käytetään vain Pythonin mukana tulevia moduuleja. Niitäkin on tosin aika monta. Sivun taulukosta voi lyhyiden kuvausten sekä moduulien nimien perusteella kohtalaisesti päätellä mihin moduulia voisi tarvita. Useimpia ei kuitenkaan välttämättä tarvitse koskaan. Itse asiassa juuri tällä hetkellä et tarvitse kuin jo käytössä olleen Turtlen, ja uutena tulokkaana math-moduulin. Sieltä löytyvät useimmat matemaattiset operaatiot, jotka eivät ole riittävän yleisiä ollakseen suoraan
sisäänrakennettujen funktioiden
joukossa. Funktioiden lisäksi moduuleista löytyy myös vakioita, joita käsitellään seuraavassa osiossa tarkemmin. Vakioita haetaan moduulista samalla tavalla kuin funktioita, eli moduulin nimi eteen, piste väliin ja vakion nii perään. Esim math-moduulista löytyvät yleiset vakiot pi ja e, joiden arvot saadaan siis seuraavasti:
In [1]: import math
In [2]: math.pi
Out[2]: 3.141592653589793
In [3]: math.e
Out[3]: 2.718281828459045
Moduulin sisältämät arvot näille vakioille ovat tarkimpia mahdollisia -
Python-tulkki
ei vain oletuksena näytä enempää desimaaleja kuin yllä. Math-moduulilla päästään leikkimään lisää harjoituksissa. Vakiot¶
Osaamistavoitteet: Tämän osion jälkeen tiedät mitä ovat vakiot, mihin niitä käytetään ja miten ne vaikuttavat muun koodin sielunelämään.
Vakio on valetta¶
Pythonissa ei oikeasti ole
vakioita
. Vakiot ovat ihan tavallisia muuttujia
, jotka erottuvat muista muuttujista ainoastaan käyttötarkoituksensa perusteella. Vakio on käytännössä nimetty arvo
, jota käytetään ohjelmakoodissa usein ja muuttumattomana. Ne ovat sukua matemaattisille ja luonnontieteellisille vakioille. Niitä voidaan käyttää muun muassa kaavojen selventämiseen: kun numeron sijaan käytetään vakion nimeä, tiedetään heti, että kyseessä ei ole mikä tahansa numero, vaan juuri tietty vakio. Näin tekemällä koodi on luettavampaa ja ymmärrettävämpää: lukijan ei tarvitse omatoimisesti koittaa keksiä mystisen luvun
merkitystä. Toiseksi, voi olla myös sellaisia tilanteita, joissa esimerkiksi sama luku esiintyy useaan kertaan ohjelmassa, mutta ohjelmoija joutuu testaamaan, mikä tarkalleen olisi sopiva sen lopulliseksi arvoksi. Tällöin on edullista käyttää vakiota, koska arvoa ei tällöin tarvitse vaihtaa kuin yhdessä kohtaa ohjelmaa.Vakiot määritellään kooditiedoston valmistelulohkossa. Tämä tapahtuu juuri samalla tavalla kuin arvojen
sijoittaminen
muuttujiin muualla koodissa. Tapana on kuitenkin kirjoittaa vakioiden nimet kokonaan isoilla kirjaimilla, mikä korostaa niiden luonnetta. VALON_NOPEUS = 299_792_458
Tässä nähdään myös Python 3.6 versiossa tullut ominaisuus: isojen lukujen kirjoituksessa voi käyttää alaviivaa tuhaterottimena, joka tekee niistä helpompia lukea.
Kun
vakiot
määritellään koodin alussa, ne ovat käytössä
kaikkialla – myös funktioiden
sisällä. Funktiot osaavat nimittäin lukea pääohjelman
määrittelemiä muuttujia, mikä mahdollistaa vakioiden käytön. Muista kuitenkin tämä: funktion sisällä ei koskaan saa käyttää mitään ulkopuolista tietoa joka ei ole a) parametri
tai b) vakio. Yhteenveto¶
Tässä luvussa on käyty läpi ohjelmoinnin aloittamisen kannalta keskeisiä perusteita.
Muuttuja
on tapa jolla tietokone muistaa arvoja
ohjelman suorituksen aikana. Pythonissa muuttuja ja sen arvo ovat erillisiä asioita: muuttuja ainoastaan viittaa
olemassaolevaan arvoon. Silti puhutaan usein kuin muuttuja sisältäisi arvon, ja erityisesti termi "muuttujan sisältö" toistuu usein. Kävimme myös läpi toisen keskeisen käsitteen eli
funktiot
. Funktiot ovat tapa jakaa ohjelmaa osiin, joita voidaan uusiokäyttää. Uusiokäyttöä tärkeämpi etu funktioissa on kuitenkin ohjelmakoodin jäsentäminen. Pienet kokonaisuudet ovat aina helpommin hallittavia kuin suuret ja rajaamattomat. Ohjelman rakentaminen funktioiden avulla antaa mahdollisuuden vastata pieniin ongelmiin ja käsitellä niitä yksi kerrallaan sen sijaan, että pyrkii suoraan ratkaisemaan yhden mammuttiongelman, josta on lähdetty liikkeelle. Funktiot ovat siis ohjelmien työnjakoa.Funktioihin liittyen tärkeäksi muodostui myös ohjelman sisäiset näkyvyysalueet. Erityisesti funktion sisäisten muuttujien erottaminen
globaaleista
eli pääohjelman
muuttujista on asia, joka vaatii paljon paneutumista ennen kuin sen sisäistää kunnolla. Funktioiden ei tulisi koskaan lukea arvoja muista muuttujista kuin niistä jotka ovat sen parametreja
. Toiseksi funktiot voivat lukea vakioiden
arvoja.Tutustuimme myös pikaisesti
moduulien
käyttöön. Moduulit ovat tapa paketoida hyödyllisiä funktioita (ja muutakin) eri käyttötarkoituksia varten. Esimerkkeinä nähtiin yksinkertaiseen piirtelyyn käytetty Turtle-moduuli sekä hieman hyödyllisempi math-moduuli. Myös moduuleihin liittyi näkyvyysaluekysymys, ja niiden kohdalla puhuttiin nimiavaruuksista
sekä siitä miten import-lauseella
voitiin päättää miten moduulin nimiavaruudesta otetaan funktioita käyttöön. Moduuleista jatketaan nelosmateriaalissa, jossa niitä opetellaan myös rakentamaan itse.Viimeisenä käsiteltiin lyhyesti
vakiot
joita käytetään ohjelmassa usein esiintyvien literaaliarvojen
korvaamiseen kahdesta syystä: nimetty vakio on kuvaavampi kuin pelkkä koodissa esiintyvä arvo; jos arvo esiintyy useissa paikoissa, on sekä ylläpidettävyyden että virheiden välttämisen kannalta edullisempaa määritellä arvo vain kerran. Kuvalähteet¶
- Ruutukaappaus History Channelin sarjasta Ancient Aliens, teksti lisätty.
- alkuperäinen lisenssi: CC-BY 2.0 (teksti lisätty)
- Ruutukaappaus elokuvasta Inception, teksti lisätty.
- alkuperäinen lisenssi: CC-BY 2.0 (teksti lisätty)
- alkuperäinen lisenssi: CC-BY 2.0 (teksti lisätty)
- alkuperäinen lisenssi: CC-BY 2.0 (teksti lisätty)
- alkuperäinen lisenssi: CC-BY 2.0 (teksti lisätty)
Anna palautetta
Kommentteja materiaalista?