Python-kielen alkeet

Tässä esitellään lyhyesti Python-kielen perusteet. Oletuksena on, että lukija on käynyt Helsingin yliopiston kurssit Ohjelmoinnin perusteet (TKT-10002) ja Ohjelmoinnin jatkokurssi (TKT-10003).

Python-ohjelman suorittaminen

Mikäli et ole asentanut vielä harjoitustyössä tarvittavia työvälineitä, tee se nyt. Ohjeet löytyvät yläpalkin valikosta kohdasta Kurssin työvälineet.

Python-ohjelmia voidaan suorittaa usealla eri tavalla. Kokeilut tehdään tyypillisesti Python-tulkissa, josta lähdekoodia kopioidaan tarvittaessa erillisiin lähdekooditiedostoihin. Python-tulkin käynnistäminen tapahtuu komentoriviltä komennolla python3.

$ python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print("Hello world!")
Hello world!
>>> muuttuja = 5
>>> print(muuttuja)
5
>>> def testi():
...     print("Testi!")
... 
>>> testi()
Testi!
>>> exit()
$

Python-kieliset ohjelmat tallennetaan .py-päätteisiin lähdekooditiedostoihin. Lähdekooditiedostot ovat tekstimuotoisia (aivan samalla tavalla kuin Java-kieliset lähdekooditiedostot). Oletetaan, että alla oleva lähdekoodi on tallennettu tiedostoon ohjelma.py ja että käyttäjä on komentoriviltä samassa kansiossa tiedoston kanssa. Tällöin ohjelman suoritus onnistuu komennolla python3 ohjelma.py -- tässä pyydetään Python-tulkkia suorittamaan tiedostossa ohjelma.py oleva lähdekoodi.

def esimerkki():
    print("Tämä on esimerkki")

esimerkki()
$ ls
ohjelma.py
$ python3 ohjelma.py
Tämä on esimerkki
$

Muuttujat ja tyypitys

Muuttujan esittely tapahtuu kirjoittamalla muuttujan nimi, yhtäsuuruusmerkki, ja muuttujalle asetettava arvo. Alla olevassa ohjelmassa luodaan muuttuja nimeltä luku, johon asetetaan arvo 42.

muuttuja = 42

Pythonissa on karkeasti ajatellen kuusi erilaista muuttujatyyppiä: luvut (kokonaisluku ja liukuluku), merkkijonot, listat, muuttumattomat listat ("tuplet"), joukot, ja sanakirjat ("Javan hajautustaulut"). Näiden lisäksi ohjelmoija voi luoda omia luokkia ja esitellä tätä kautta uusia muuttujatyyppejä.

kokonaisluku = 42
print(kokonaisluku)
print(type(kokonaisluku))

liukuluku = 3.14159
print(liukuluku)
print(type(liukuluku))

merkkijono = "42/3.14159 ~ 13.37"
print(merkkijono)
print(type(merkkijono))

muuttumatonlista = (42, 21, 10.5)
print(muuttumatonlista)
print(type(muuttumatonlista))

lista = [42, 21, 10.5]
print(lista)
print(type(lista))

joukko = {"123", "456", 42}
print(joukko)
print(type(joukko))

sanakirja = {"eka":1, "toka": 2}
print(sanakirja)
print(type(sanakirja))
42
<type 'int'>
3.14159
<type 'float'>
42/3.14159 ~ 13.37
<type 'str'>
(42, 21, 10.5)
<type 'tuple'>
[42, 21, 10.5]
<type 'list'>
set([42, '123', '456'])
<type 'set'>
{'toka': 2, 'eka': 1}
<type 'dict'>
Python on dynaamisesti tyypitetty kieli

Dynaamisesti tyypitetyssä kielessä muuttujan nimi on linkitetty olioon tai null-viitteeseen. Tämä tarkoittaa sitä, että muuttujan arvon muuttaminen -- eli nimen linkittäminen johonkin toiseen olioon -- muuttaa myös muuttujan tyyppiä.

Vahvasti tyypitetyssä kielessä kuten Java muuttujan nimeen on linkitetty sekä tyyppi että olio. Tämä tarkoittaa sitä, että muuttujan tyyppi ei voi muuttua sen jälkeen kun se on kertaalleen asetettu.

Koska Python on dynaamisesti tyypitetty kieli, on myös seuraavanlainen ohjelmointi mahdollista.

muuttuja = 42
print(muuttuja)
print(type(muuttuja))

muuttuja = 3.14159
print(muuttuja)
print(type(muuttuja))

muuttuja = "42/3.14159 ~ 13.37"
print(muuttuja)
print(type(muuttuja))

muuttuja = (42, 21, 10.5)
print(muuttuja)
print(type(muuttuja))

muuttuja = [42, 21, 10.5]
print(muuttuja)
print(type(muuttuja))

muuttuja = {"123", "456", 42}
print(muuttuja)
print(type(muuttuja))

muuttuja = {"eka":1, "toka": 2}
print(muuttuja)
print(type(muuttuja))
42
<type 'int'>
3.14159
<type 'float'>
42/3.14159 ~ 13.37
<type 'str'>
(42, 21, 10.5)
<type 'tuple'>
[42, 21, 10.5]
<type 'list'>
set([42, '123', '456'])
<type 'set'>
{'toka': 2, 'eka': 1}
<type 'dict'>

Yllä olevassa esimerkissä demonstroidaan sekä pythonin muuttujia että dynaamista tyypitystä. Muuttujan tyyppiä voi vaihtaa asettamalla siihen uuden arvon.

Muuttujien elinkaari on Pythonissa ja Javassa samankaltainen. Muuttuja on olemassa sen esittelyhetkestä lähtien siihen asti kunnes muuttujaan ei enää viitata ohjelmakoodissa. Pythonissa, kuten Javassa, on automaattinen roskienkerääjä, joka vapauttaa muuttujia varten varatun muistin kun niihin ei enää viitata.

Nimeämiskäytänteet

Pythonissa muuttujien, metodien, funktioiden, luokkien ym nimet aloitetaan kirjaimella (A-Z tai a-z) tai alaviivalla (_), joita seuraa nollasta äärettömään määrä kirjaimia, alaviivoja tai numeroita. Python-ohjelmoinnissa noudatetaan yleisesti seuraavia nimeämiskäytänteitä:

  • Luokkien nimet aloitetaan isolla kirjaimella. Kaikki muut muuttujat aloitetaan pienellä kirjaimella.
  • Yhdellä alaviivalla aloitettu muuttujan nimi tarkoittaa, että muuttujaa tulee ajatella kapseloituna (Javan private). Esimerkiksi muuttuja nimeltä "_ika" olisi oliomuuttuja, jota tulee käsitellä vain olion tarjoamista metodeista.
  • Kapselointia voi korostaa kahdella alaviivalla. Esimerkiksi muuttujaa nimeltä "__ika" ei missään nimessä tule käsitellä muualta kuin olion tarjoamista metodeista.

Python on case-sensitive, eli pienillä kirjaimilla kirjoitettuun muuttujan nimeen ei voi viitata isoilla kirjaimilla kirjoitetulla muuttujan nimellä. Käytännössä siis "muuttuja" on eri kuin "Muuttuja" tai "MUUTTUJA".

Kontrollirakenteet

Kontrollirakenteilla ohjataan ohjelman suoritusta. Pythonissa on ohjelmointikielille tyypilliset ehtolauseet ja toistolauseet.

Ehtolause

Ehtolause if toteutetaan Pythonissa seuraavasti.

muuttuja = 42

if muuttuja == 42:
    print("Muuttujan arvo oli 42")
Muuttujan arvo oli 42

Ehtolauseen lauseketta ei ympäröidä suluilla eikä ehtolauseen lohkoa rajata aaltosuluilla. Ehtolauseen lauseke päättyy kaksoispisteeseen, jota seuraa suoritettava lohko. Ohjelmakoodin sisennys kertoo lohkoon kuuluvat lauseet.

muuttuja = 42

if muuttuja != 42:
    print("Muuttujan arvo oli 42")
    print("Jei!")

print("Ok!")
Ok!
Sisennys kertoo lohkosta, johon rivi kuuluu

Python-kielessä sisennyksellä on kriittinen rooli ohjelman toiminnallisuuden kannalta. Sisennys määrittelee lohkon, johon kukin rivi kuuluu. Mikäli sisennys on väärin, näemme virheen IndentationError.

Sisennys on tyypillisesti neljä välilyöntiä. Alla olevassa esimerkissä sisennys on virheellinen.

muuttuja = 42

if muuttuja == 42:
    print("Muuttujan arvo oli 42")
   print("Virheellinen sisennys")

print("Ok!")

Ohjelman suorittaminen antaa virheen:

  File "ohjelma.py", line 6
    print("Virheellinen sisennys")
                                 ^
IndentationError: unindent does not match any outer indentation level

Alla olevassa esimerkissä taas sisennys on kunnossa.

muuttuja = 42

if muuttuja == 42:
    print("Muuttujan arvo oli 42")
    print("Oikein oleva sisennys")

print("Ok!")

Sisennyksiä voi olla sisäkkäin useampia. Alla oleva esimerkki on Python-kielen näkökulmasta oikein toimiva.

muuttuja = 42
toinen = 13

if muuttuja == 42:
    print("Muuttujan arvo oli 42")
    print("Oikein oleva sisennys")

    if toinen == 13:
        print("Toisen muuttujan arvo oli 13")
    
print("Ok!")
Muuttujan arvo oli 42
Oikein oleva sisennys
Toisen muuttujan arvo oli 13
Ok!

Javasta tutut if, else if ja else löytyvät Pythonissa nimillä if, elif ja else.

muuttuja = 13

if muuttuja == 42:
    print("Muuttujan arvo oli 42")
elif muuttuja == 13:
    print("Muuttujan arvo oli 13")
else:
    print("Muuttujan arvo ei ollut 42 tai 13")
  
print("Ok!")

Pythonissa on käytössä kaikki Javasta tutut loogiset operaatiot: pienempi kuin <, suurempi kuin >, pienempi tai yhtäsuuri kuin <=, suurempi tai yhtä suuri kuin >=, yhtä suuri kuin == ja erisuuri kuin !=.

Javasta poiketen Python käyttää lausekkeiden vertailussa ja ja tai sanojen englanninkielisiä vastineita. Javan || merkitään Pythonissa or. Vastaavasti Javan && merkitään Pythonissa and.

Suluilla voidaan ryhmitellä vertailuja -- alla oleva esimerkki tarkastaa onko muuttujan vuosi arvo karkausvuosi.

vuosi = 2020

if vuosi % 4 == 0 and (vuosi % 100 != 0 or vuosi % 400 == 0):
    print("Annettu vuosi oli karkausvuosi.")

Toistolause

Pythonista löytyy sekä while että for -toistolauseet. Pythonissa on myös avainsanat continue, jonka suorittaminen siirtää ohjelman suorituksen toistolauseen alkuun, sekä break, jonka suorittaminen lopettaa toistolauseen suorituksen jolloin seuraava suoritettava lause on toistolauseen lohkoa seuraava rivi.

Klassinen while-true -toistolause kirjoitetaan seuraavasti. Pythonissa on valmiina totuusarvoinen muuttuja True, joka vastaa Javan true-arvoa.

arvo = 0

while True:
    print(arvo)
    arvo += 1

    if arvo == 3:
        break
0
1
2

Toistolauseen while avainsanaa seuraa lauseke, jonka evaluoinnin tuloksena saatu totuusarvo kertoo jatketaanko toistolauseen suoritusta. Yllä olevassa esimerkissä lausekkeen arvo on aina totta. Lausekkeen voi kirjoittaa myös auki ehtona -- alla olevan esimerkin tulostus vastaa edellä olevaa esimerkkiä.

arvo = 0

while arvo != 3:
    print(arvo)
    arvo += 1

Pythonin for-toistolause poikkeaa Javan for-lauseesta: se muistuttaa enemmänkin Javan for-each-lausetta.

Pythonin for-toistolauseella käydään kokoelmia läpi. Lukujen läpikäynti onnistuu erillisen range-funktion avulla, joka palauttaa luvut nollasta yhtä annettua arvoa pienempään asti.

Alla olevassa esimerkissä tulostetaan for-lauseen avulla luvut 0, 1 ja 2.

for arvo in range(3):
    print(arvo)
0
1
2

Tietorakenteet

Lista

Listaan voi lisätä tietoa ja listasta voi poistaa tietoa. Listan koko on käytännössä rajoittamaton, eli se kasvaa sitä mukaa kun sinne lisätään tietoa. Pythonin lista on toteutukseltaan Javan ArrayListin kaltainen.

Listamuuttuja luodaan hakasulkeilla []. Arvojen lisääminen listaan onnistuu määrittelemällä arvot hakasulkeiden sisälle. Tietyssä listan indeksissä olevaan arvoon viitataan samalla tavalla kuin Javan taulukoissa eli hakasulkeiden avulla. Alla olevassa esimerkissä luodaan neljä arvoa sisältävä lista ja tulostetaan ensimmäisessä ja kolmannessa indeksissä olevat arvot.

lista = [100, 10, 1, 0]
print(lista[1])
print(lista[3])
10
0

Python tarjoaa listojen käsittelyyn joukon metodeja, joista alla muutama.

  • lista.append(arvo) lisää arvo listan loppuun.
  • lista.insert(indeksi, arvo) siirtää annetussa indeksissä ja indeksiä seuraavissa indekseissä olevia arvoja yhden eteenpäin ja asettaa annetun arvon indeksiin
  • lista.index(arvo) etsii annettua arvoa listasta ja palauttaa arvon indeksin mikäli se löytyy. Mikäli arvoa ei löydy, heittää poikkeuksen.
  • lista.remove(arvo) etsii annettua arvoa listasta ja poistaa arvon mikäli se löytyy. Mikäli arvoa ei löydy, heittää poikkeuksen.

Lisää metodeja löytyy Pythonin dokumentaatiosta https://docs.python.org/3/tutorial/datastructures.html.

Metodien käyttöä on demonstroitu alla.

lista = []
lista.append(5)
lista.append(2.5)
lista.append(1.25)

print(lista.index(1.25))
print(lista[lista.index(1.25)])

lista.remove(5)

print(lista.index(1.25))
print(lista[lista.index(1.25)])

lista.insert(0, 100)

print(lista.index(1.25))
print(lista[lista.index(1.25)])
2
1.25
1
1.25
2
1.25

Listan läpikäynti onnistuu for-lauseen avulla suoraviivaisesti.

lista = [100, 10, 1, 0]
for luku in lista:
    print(luku)
100
10
1
0

Muuttumaton lista

Listasta on mahdollista tehdä myös muuttumaton lista. Muuttumaton lista esitellään sulkujen avulla.

lista = (100, 10, 1, 0)
print(lista[0])
print(lista[2])
  
for luku in lista:
    print(luku)
100
1
100
10
1
0

Muuttumaton lista poikkeaa tavallisesta listasta siinä, että sillä on vain rajattu joukko tavallisen listan metodeja käytössään. Esimerkiksi lisääminen tai poistaminen aiheuttaa virheen.

lista = (100, 10, 1, 0)
lista.append(-1)
Traceback (most recent call last):
  File "ohjelma.py", line 2, in <module>
    lista.append(-1)
AttributeError: 'tuple' object has no attribute 'append'

Yllä olevasta virheviestistä huomaamme, että kyseessä ei ole oikeastaan lista vaan monikko (tuple). Muuttumattomien listojen käyttö on Pythonissa tyypillistä, kun haluamme esimerkiksi palauttaa funktiosta arvopareja.

Joukko ja sanakirja

Python tarjoaa myös Javan HashSet ja HashMap-luokkia vastaavat tietorakenteet. Pythonissa nämä ovat set ja dictionary.

joukko = {1, 3, 5, 5, 3, 1}
print(joukko)

sanakirja = {"yksi": 1, "kolme": 3}
print(sanakirja)
set([1, 3, 5])
{'yksi': 1, 'kolme': 3}

Näistäkin tietorakenteista löytyy lisää tietoa Pythonin dokumentaatiosta osoitteesta https://docs.python.org/3/tutorial/datastructures.html.

Funktiot

Kuten Javassa, Pythonissa funktiot ovat suoritettavia nimettyjä lähdekoodilohkoja. Funktion määrittely Pythonissa tapahtuu avainsanalla def, jota seuraa funktion nimi, sulut, mahdolliset parametrit sekä kaksoispiste. Kaksoispisteen jälkeen alkaa lohko, joka sisältää funktion lähdekoodin.

def tulostaHei():
    print("Hei!")

Kun funktio on esitelty, sen kutsuminen tapahtuu nimen perusteella. Esimerkiksi yllä kuvattua funktiota tulostaHei kutsutaan kutsulla tulostaHei().

tulostaHei()
Hei!

Parametrien määrittely tapahtuu lisäämällä parametrit funktion määrittelyn yhteydessä annettujen sulkujen sisään. Alla olevassa esimerkissä luodaan funktio, joka tulostaa merkkijonon "Hei!" parametrina annetusta luvusta riippuvan määrän.

def tulosta(montakoKertaa):
    for kerta in range(montakoKertaa):
        print("Hei!")

Funktion kutsu:

tulosta(5)
Hei!
Hei!
Hei!
Hei!
Hei!

Funktiolle voidaan määritellä useampi parametri. Parametrit erotellaan toisistaan pilkuilla. Alla edellistä funktiota on muokattu siten, että funktiolle annetaan parametrina lukumäärän lisäksi myös tulostettava arvo.

def tulosta(montakoKertaa, tulostettava):
    for kerta in range(montakoKertaa):
        print(tulostettava)
tulosta(3, "Hei")
Hei
Hei
Hei

Toisin kuin Javassa, Pythonissa funktioiden parametreille voidaan määritellä myös oletusarvot. Näitä arvoja käytetään mikäli funktiokutsun yhteydessä ei anneta parametrille arvoa.

Oletusarvojen määrittely tapahtuu parametrien määrittelyn yhteydessä yhtäsuuruusmerkillä. Alla määriteltävä funktio tulosta tulostaa oletuksena merkkijonon "Hei" kerran.

def tulosta(montakoKertaa = 1, tulostettava = "Hei"):
    for kerta in range(montakoKertaa):
        print(tulostettava)
tulosta()
Hei
tulosta(2)
Hei
Hei

Huomaa, että parametrien järjestyksellä on väliä. Seuraava kutsu aiheuttaa virheen.

tulosta("Heippa")
Traceback (most recent call last):
  File "ohjelma.py", line 5, in <module>
    tulosta("Heippa")
  File "ohjelma.py", line 2, in tulosta
    for kerta in range(montakoKertaa):
TypeError: range() integer end argument expected, got str.

Parametrit voi toisaalta myös nimetä funktiokutsun yhteydessä. Alla edellinen esimerkki uudestaan toimivana.

tulosta(tulostettava="Heippa")
Heippa

Vaikka kaikki Pythonin muuttujat ovat olioita, on Pythonissa silti näkyvissä Javan kaltainen käyttäytyminen alkeis- ja viittaustyyppisiin muuttujiin liittyen. Käytännössä Pythonin luvut ovat muuttumattomia (immutable), ja niiden muuttaminen luo uuden olion.

Kun funktiokutsussa annetaan parametri, parametrin arvo kopioituu suoritettavan funktion käyttöön funktion suorituksen ajaksi. Tämä käytännössä tarkoittaa sitä, että muuttumattomien muuttujien käsittely funktion sisällä ei vaikuta muuttujan arvoon funktion ulkopuolella, kun taas muuttuvien muuttujien käsittely voi vaikuttaa myös funktion ulkopuolella olevaan muuttujan arvoon.

def lisaaListaan(lista):
    lista.append(1)

def lisaaLukuun(luku):
    luku += 1
lista = []
print(lista)
lisaaListaan(lista)
print(lista)

luku = 0
print(luku)
lisaaLukuun(luku)
print(luku)
[]
[1]
0
0

Kuten Javassa, funktiosta voi palauttaa arvon return-komennolla.

def lisaaLukuun(luku):
    luku += 1
    return luku
luku = 0
print(luku)
lisaaLukuun(luku)
print(luku)
luku = lisaaLukuun(luku)
print(luku)
0
0
1

Lisää funktioiden määrittelystä osoitteessa https://docs.python.org/3/tutorial/controlflow.html#defining-functions.

Olio-ohjelmointi

Python tarjoaa välineet myös olio-ohjelmointiin. Kuten Javassa, luokan luominen Pythonissa luo kieleen uuden muuttujatyypin, johon voidaan viitata jatkossa. Luokka luodaan avainsanalla class, jota seuraa luokan nimi sekä kaksoispiste. Kaksoispistettä seuraa luokan sisältämä lohko.

Alla luodaan luokka Henkilo sekä määritellään sille konstruktori. Konstruktori on aina nimeltä __init__ ja se saa parametrinaan vähintään self-viitteen. Viite self on Javan this-viitteen kaltainen ja se viittaa aina käsiteltävään olioon.

Javasta poiketen, Pythonissa oliomuuttujat määritellään tyypillisesti osana konstruktoria.

class Henkilo:
    def __init__(self):
        self.nimi = "Matti Meikäläinen"

Konstruktoria kutsutaan luokan nimen kautta. Alla olevassa esimerkissä luodaan uusi henkilöolio, joka tulostetaan.

h = Henkilo()
print(h)
<__main__.Henkilo object at 0x7f8cb49e8898>

Pythonissa Javan toString-metodia vastaa metodi __str__. Muokataan luokkaa Henkilo siten, että henkilön tulostaminen tulostaa henkilön nimen.

class Henkilo:
    def __init__(self):
        self.nimi = "Matti Meikäläinen"

    def __str__(self):
        return self.nimi

Nyt tulostus on ymmärrettävämpi.

h = Henkilo()
print(h)
Matti Meikäläinen

Olioon liittyvien metodien määrittely tapahtuu määrittelemällä self-parametrillisia funktioita luokan sisälle. Alla luokan Henkilo konstruktoria on muutettu siten, että nimen voi halutessaan antaa parametrina. Tämän lisäksi henkilöllä on ikä, joka muuttuu metodia vanhene kutsuttaessa.

class Henkilo:
    def __init__(self, nimi = "Matti Meikäläinen"):
        self.nimi = nimi
        self.ika = 0

    def vanhene(self):
        self.ika += 1

    def __str__(self):
        return self.nimi + ", ikä: " + str(self.ika)
matti = Henkilo()
print(matti)
matti.vanhene()
print(matti)

maija = Henkilo("Maija Meikäläinen")
print(maija)
Matti Meikäläinen, ikä: 0
Matti Meikäläinen, ikä: 1
Maija Meikäläinen, ikä: 0

Yllä esitetyllä tavalla esiteltyjä muuttujia voi muuttaa suoraan. Alla Maijan iäksi asetetaan 10.

maija = Henkilo("Maija Meikäläinen")
maija.ika = 10
print(maija)
Maija Meikäläinen, ikä: 10

Mikäli muuttujista halutaan Javan private-tyyppistä muuttujaa vastaavat, eli sellaiset ettei niitä voida käsitellä suoraan luokan ulkopuolelta, lisätään muuttujien nimen alkuun kaksi alaviivaa.

class Henkilo:
    def __init__(self, nimi = "Matti Meikäläinen"):
        self.__nimi = nimi
        self.__ika = 0

    def vanhene(self):
        self.__ika += 1

    def __str__(self):
        return self.__nimi + ", ikä: " + str(self.__ika)

Nyt iän muuttaminen ei onnistu suoraan, mutta vanheneminen on yhä mahdollista.

maija = Henkilo("Maija Meikäläinen")
maija.ika = 10
maija.__ika = 10
print(maija)
maija.vanhene()
print(maija)
Maija Meikäläinen, ikä: 0
Maija Meikäläinen, ikä: 1

Lisää luokkien ja olioiden luomisesta löytyy Pythonin dokumentaatiosta osoitteessa https://docs.python.org/3/tutorial/classes.html.

Sisällysluettelo