Python-kielen perusteita
- Python
- Python
- Poikkeukset
- Sanakirja (dict)
- Monikot (tuple)
- Joukko (set)
- Luokat
- Tietorakenteita
- Tiedostojen käsitteleminen
- Serialisointi
- Esimerkkiohjelmia
- Luentovideoita
Python
Python-kielestä on kaksi versiota: Python 2 ja Python 3. Käytämme tällä kurssilla Python 3 -kieltä. Osa esimerkeistä on kirjoitettu vanhemmalla Python 2 -kielellä, mutta ne on helposti muunnettavissa Python 3 -yhteensopiviksi.
Python 3:sta uusin versio on 3.10.1. Käytämme tällä kurssilla hieman vanhempaa Python 3.8.8. Näissä ei ole merkittäviä eroja.
Python 2 ja Python 3 erot
Tärkeimmät eroavaisuudet:
Python 3 | Python 2 | |
---|---|---|
print-funktio | print("foo") | print "foo" |
Kokonaislukujen jakolasku | Kokonaislukujen jakolaskun tulos on liukuluku
| Kokonaislukujen jakolaskun tulos on kokonaisluku
|
Unicode | Merkkijonot ovat oletuksena unicode
| unicode-merkkijonojen edessä täytyy olla u
|
iterointi | range | xrange |
Python on korkean tason ohjelmointikieli, jonka suunnittelussa on panostettu erityisesti kielen luettavuuteen. Monesta muusta kielestä poiketen Python käyttää koodin sisennyksiä koodilohkojen merkintään.
Python
Pythonin syntaksiin kannattaa tutustua esim. seuraavien tutoriaalien ja ohjeiden avulla:
- Python tutorial (v3.8)
- Pythonin perusteita (TIM)
- Python 3.8 documentation
- Python FAQ
- PEP 0008 - Style Guide for Python Code
- Portin Python 2 code to Python 3
Seuraavassa on käyty läpi tärkeimpiä Pythonin piirteitä.
- Asenna omalle koneellesi uusin Python 3.10.1
- Pythonia voi kokeilla komentoriviltä Linux- ja Unix-ympäristöissä kirjoittamalla: python3 tai python3.8
IT-palvelujen halava- ja jalava-koneissa python3 löytyy polusta:
/opt/rh/rh-python38/root/usr/bin/python3.8
Pythonin objektien ominaisuuksia ja avustusta voi lukea kirjoittamalla help ja help(objekti)
[tjlahton@halava ~]$ /opt/rh/rh-python38/root/usr/bin/python3.8 Python 3.8.11 (default, Jul 23 2021, 14:55:16) [GCC 9.1.1 20190605 (Red Hat 9.1.1-2)] on linux Type "help", "copyright", "credits" or "license" for more information. >>>
- Suoritettavan python-ohjelman voi tehdä, kun määrää ohjelmakooditiedoston shebang-rivin viittaamaan
python-tulkkiin ja asettaa tiedostolle suoritusoikeudet.
Ohjelmakoodi:
#!/opt/rh/rh-python38/root/usr/bin/python3.8 # -*- coding: utf-8 -*- print("Hello world!")
Yleensä kannattaa myös määritellä ohjelmakoodissa käytetty merkistö # -*- coding: utf-8 -*-. Python3 olettaa, että ohjelmakoodin merkistö on UTF-8, jollei muuta ilmoiteta.
Ohjelman suorittaminen komentoriviltä tapahtuu antamalla tiedostolle suoritusoikeus ja sen jälkeen suorittamalla kyseinen tiedosto:
[tjlahton@halava ~/python]$ chmod a+rx malli.py [tjlahton@halava ~/python]$ ./malli.py Hello world!
- #-merkillä aloitetaan kommentit
- isot ja pienet kirjaimet ovat merkityksellisiä
- Jos kirjoitat useamman komennon samalle riville niin ne erotetaan toisistaan puolipisteellä (;). Jos komennot ovat omilla rivillään ei mitään erotinmerkkiä tarvita. Suositeltavaa on kirjoittaa koodia tavalla, jossa puolipisteitä ei tarvita. Lue PEP 8 -- Style Guide for Python Code
- Välilyönnit (sisennys) rivin alussa ovat Pythonissa merkitseviä. Loogisesti samaan ryhmään kuuluvat koodirivit
pitää sisentää samalle tasolle.
- Älä sisennä tabulaattorimerkillä vaan vain välilyönneillä
Tarkista editorisi asetuksista, että tabulaattori korvataan neljällä välilyönnillä
Suositus on sisentää neljä merkkiä kerrallaan - Pyri pitämään rivin maksimipituutena 79 merkkiä
- Älä sisennä tabulaattorimerkillä vaan vain välilyönneillä
- Käytä UNIX-tyyppisiä rivinvaihtoja eikä DOS-tyyppisiä.
Muuttujat
- Muuttujia ei tarvitse erikseen esitellä
- Muuttujille ei tarvitse määrittää tyyppiä vaan tyyppi määräytyy automaattisesti
- Muuttujien tyyppiä joutuu kuitenkin joskus muuntamaan seuraavilla funktioilla.
int(x [,base])
muuntaa x:n kokonaisluvuksifloat(x)
muuntaa x:n liukuluvuksistr(x)
muuntaa x:n unicode merkkijonoksi
Merkkijonot
Python 2.7:ssa oli hämäävästi kahta eri tyyppiä olevia merkkijonoja: str ja unicode. Python 3:ssa on vain unicode-merkkijonoja ja tavujonoja.
>>> foo = "testi"
>>> bar = b"testi"
>>> type(foo)
<class 'str'>
>>> type(bar)
<class 'bytes'>
Pythonissa merkkijonoja voidaan esittää seuraavilla tavoilla:
- Käyttäen sitaattimerkiä (')
- Käyttäen lainausmerkkiä (")
- Käyttäen kolmea sitaatti- tai lainausmerkkiä (''', """) voit kirjoittaa useamman rivin pituisia merkkijonoja
- Merkkijonojen sisällä olevat erikoismerkit (',", \) merkitään \-merkin avulla esim. \' tai \" tai \\
- Merkkijonojen yhdistäminen tapahtuu kirjoittamalla merkkijonot peräkkäin esim.
tai voi käyttää +-operaattoriaprint('kukku' 'luuru')
print('kukku' + 'luuru')
- Merkkijonoja voidaan palastella seuraavasti:
word = "abcdefghijklmnopqrstuvwxyz" print( word[4]) #tulostaa e print( word[0:2]) #tulostaa ab print( word[3:5]) #tulostaa de print( word[:5]) #tulostaa abcde print( word[3:]) #tulosta defghijklmnopqrstuvwxyz
Indeksit voivat olla myös negatiivisia, jolloin ne lasketaan oikealta.
- Merkkijonoja ei voi muuttaa
word[4] = "q" # aiheuttaa virheen
- Merkkijonofunktioita
- str.find(sub[, start[, end]])
- str.isalnum()
- str.isalpha()
- str.isdigit()
- str.join(iterable)
- str.lower()
- str.lstrip([chars])
- str.replace(old, new[, count])
- str.rfind(sub[, start[, end]])
- str.rstrip([chars])
- str.split([sep[, maxsplit]])
- str.splitlines([keepends])
- str.strip([chars])
- str.upper()
Listat
Lista on pythonissa vastaava rakenne kuin esim. Javascriptin taulukko
Listojen palastelu tapahtuu samaan tapaan kuin merkkijonoilla.
Listoja voi muuttaa päinvastoin kuin merkkijonoja
a = [1, 2, 3, 'foo', 'bar']
a[0] = 3 # [3, 2, 3, 'foo', 'bar']
a[1] = a[2] * 3 #[3, 9, 3, 'foo', 'bar']
a[2:1] = ['kukku','luuru'] # [3, 9, 3, 'kukku', 'luuru', 'foo', 'bar']
Yhteisiä funktioita merkkijonoille ja listoille:
x in y
Tosi, jos jokin y:n alkiousta on sama kuin x, muuten epätosix not in y
Epätosi, jos jokin y:n alkioista on sama kuin x, muuten tosis + t
konkatenoi (yhdistää peräkkäin) s ja ts[i]
i item of s, origin 0s[i:j]
slice of s from i to js[i:j:k]
slice of s from i to j with step klen(s)
palauttaa s:n pituudenmin(s)
palauttaa s:n pienimmän alkionmax(s)
palauttaa s:n suurimman alkion
Operaattorit
Seuraavassa on listattu Pythonin tärkeimmät operaattorit:
- = sijoitus
- + yhteenlasku
- - vähennyslasku
- * tulo
- ** potenssi
- / jako
- // tasajako eli kertoo kuinka monesti y menee x:ään
- % jakojäännös
- <
- >
- <=
- >=
- ==
- !=
- not boolean NOT
- and boolean AND
- or boolean OR
if
a = 3
if a> 0:
print("a on suurempi kuin 0")
elif a < 0:
print("a on pienempi kuin 0")
else:
print("a on 0")
Toistorakenteet
laskuri = 12
while laskuri> 0:
laskuri = laskuri - 1
a = [1,2,3,'foo','bar']
for x in a:
print( x )
for x in a[:]: #tekee kopion a:sta. tarpeellinen jos aikoo muuttaa listaa
print( x )
for i in range(1, 5): #range-funktio luo listan halutulta numeroväliltä
print( i )
Pythonissa ei ole do..while-toistorakennetta.
for käy järjestyksessä läpi rakenteen kaikki alkiot. vrt. joissain kielissä oleva foreach.
Läpikäytävää rakennetta ei pidä muuttaa läpikäynnin aikana. Jos muutoksia pitää tehdä, niin on syytä käydä läpi rakenteen kopiota tai käydä listaa läpi lopusta alkuun.
#!/opt/rh/rh-python38/root/usr/bin/python3.8
luvut = [1,2,3,4,5,5,6,7,8,9,9,10,10,10]
#tulosta kaikki luvut
for luku in luvut:
print(luku)
#tulosta joka toinen luku
vaihto = False
for luku in luvut:
if vaihto:
print(luku)
vaihto = not vaihto
#tulosta parilliset luvut
for luku in luvut:
if luku % 2 == 0:
print(luku)
#tulosta kaikki luvut
for i in range(len(luvut)):
print(luvut[i])
#poista joka toinen luku
#poistamiseen tarvitaan luvun paikka listassa
#tämä toimii väärin!
#vaihto = False
#for i in range(len(luvut)):
# if vaihto:
# del luvut[i]
# vaihto = not vaihto
#Tämä toimii oikein. Käydään lista läpi lopusta alkuun
vaihto = False
for i in reversed( range(len(luvut)) ):
if vaihto:
del luvut[i]
vaihto = not vaihto
#poista parilliset luvut
#seuraava toimii vääriin. Pitää käydä lopusta alkuun
#for i in range(len(luvut)):
#tämä on oikein
for i in reversed(range(len(luvut))):
if luvut[i] % 2 == 0:
del luvut[i]
Funktiot
def summa(a, b): # laskee lukujen summan
summa = a + b
return summa
Funktioiden parametrit ovat referenssejä, joten niiden arvojen muuttaminen vaikuttaa suoraan kutsuvaan ohjelmaan (call-by-reference).
Jos parametrina vie immutable-tyyppisiä muuttujia, niin niitä ei tietenkään voi muuttaa funktiossa. Esim. kokonaisluvut, merkkijonot ja tuplet ovat immutable. Kts. Parameter Passing
#!/opt/rh/rh-python38/root/usr/bin/python3.8
a = "kissa"
print(a)
#merkkijonot ovat immutable eli niitä ei voi muuttaa
def koira(p):
p = "kissakoira"
return "koira"
ret = koira(a)
print(ret)
# tämä on edelleen kissa
print(a)
lista = ["kissa", "kissa", "kissa"]
print(lista)
kissa = lista[0]
print(kissa)
#listoja voi muuttaa
def listakoira(l):
l[0] = "kissakoira" #korvaa listan ensimmäisen alkion uudella merkkijonolla
return "koira"
ret = listakoira(lista)
print(ret)
print(kissa)
print(lista)
Moduulit
Pythonissa voi jakaa ohjelmansa moduuleihin tai käyttää valmiita moduuleja
import cgi
Oman moduulin voi luoda kirjoittamalla python-funktioita tiedostoon, jonka nimi päättyy .py
Poikkeukset
Pythonissa lähes kaikki virheet aiheuttavat poikkeuksen. Poikkeukset käsitellään seuraavasti:
#!/usr/bin/python3.7
# -*- coding: utf-8 -*-
try:
if int("a"):
print("onpas kummaa. a oli kokonaisluk")
#nappaa kiinni kaikki mahdolliset virheet
except:
print("ei ollunna integer")
# edellä oleva ei ole hyvä debuggaamisen kannalta koska nappaa kiinni
# myös kirjoitusvirheet funktioiden nimissä ja python-tulkin oma
# virheilmoitus jää näyttämättä
try:
if Int("a"): # tulee virhe koska Int ei ole olemassa
print("onpas kummaa. a oli kokonaisluk")
#nappaa kiinni kaikki mahdolliset virheet
except:
print("ei ollunna integer")
# parempi napata kiinni vain se olennainen virhe
try:
if int("a"):
print("onpas kummaa. a oli kokonaisluk")
except ValueError:
print("ei ollunna integer")
# nyt pitäisi toimia tulkin oma virheilmoitus
try:
if Int("a"):
print("onpas kummaa. a oli kokonaisluk")
except ValueError:
print("ei ollunna integer")
kts. Idioms and Anti-Idioms in Python
Sanakirja (dict)
Pythonissa on erittäin kätevä tietotyyppi dict eli sanakirja, jota muissa ohjelmointikielissä kutsutaan mm. assosiatiiviseksi taulukoksi. Javascriptissa vastaava on Object tai Map. dict-tietotyyppi on siis järjestämätön taulukko, jossa alkioihin viitataan uniikeilla avaimilla.
Python 3.8.11 (default, Jul 23 2021, 14:55:16)
[GCC 9.1.1 20190605 (Red Hat 9.1.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> testi = dict([[1,2], [2,2], ['kolme',3], ['neljä',4]])
>>> testi
{1: 2, 2: 2, 'kolme': 3, 'neljä': 4}
>>> for x in testi.keys(): print(x)
...
1
2
kolme
neljä
>>> for x in testi.keys(): print(testi[x])
...
2
2
3
4
>>> testi['kolme']
3
>>> testi[2]
2
>>> testi[1]
2
>>>
Monikot (tuple)
Monikko eli tuple on listaa vastaava tietorakenne, jonka sisältöä ei voi muuttaa.
a = (1,2,3,"testi")
print(a)
a=[0] # ei onnistu
Joukko (set)
Pythonin erikoisempiin tietotyyppeihin kuuluu set eli joukko. Joukko ei voi sisältää duplikaatteja ja siihen voi soveltaa joukko-opin operaatioita kuten yhdiste, leikkaus, erotus jne.
>>> lista = ['tommi', 'antti', 'ville', 'tommi']
>>> joukko = set(lista)
>>> joukko
set(['ville', 'antti', 'tommi'])
>>> 'tommi' in joukko
True
>>> 'kalle' in joukko
False
>>> joukko2 = set(['joonas','antti','kalle'])
>>> joukko2
set(['joonas', 'kalle', 'antti'])
>>> joukko - joukko2 #henkilöt joukosta jotka eivät ole joukko2:ssa
set(['ville', 'tommi'])
>>> joukko & joukko2 #henkilöt jotka ovat molemmissa joukoissa
set(['antti'])
>>> joukko ^ joukko2 #henkilöt jotka ovat jommassa kummassa joukossa mutta eivät molemmissa
set(['joonas', 'kalle', 'tommi', 'ville'])
>>> joukko | joukko2 #henkilöt jotka ovat jommassa kummassa joukossa
set(['ville', 'joonas', 'kalle', 'antti', 'tommi'])
Luokat
Luokat toimivat kuten muissakin kielissä. Muutama erikoisuus on syytä huomioida. Lue Class tutorial
#!/usr/bin/python3.7
# -*- coding: utf-8 -*-
#suoraan luokan alla esitellyt muuttujat ovat luokkakohtaisia muuttujia. Tämän huomaa
#jos käyttää tietotyyppejä, jotka sallivat sisältönsä muuttamisen
class Ihminen:
nimi = "Kalle"
tiedot = []
def __init__(self, sukupuoli="Nainen"):
self.data = [sukupuoli]
def tulosta(self):
# on käytettävä self
print(self.nimi, self.tiedot, self.data)
mies = Ihminen("Mies")
nainen = Ihminen()
mies.nimi = "Tommi"
mies.tiedot.append("180 cm")
mies.tiedot.append("75 kg")
print(mies.nimi)
nainen.nimi = "Hellu"
print(nainen.nimi)
print(mies.nimi)
# molemmilla on samat tiedot!
print(mies.tiedot)
print(nainen.tiedot)
nainen.tiedot.append("170 cm")
nainen.tiedot.append("75 kg")
print(mies.tiedot)
print(nainen.tiedot)
# data on kummallakin oma, koska se on esitelty konstruktorissa
print(mies.data)
print(nainen.data)
mies.data.append("30 v")
nainen.data.append("35 v")
print(mies.data)
print(nainen.data)
# voidaan myös lennosta keksiä uusia ominaisuuksia vrt. Javascript
# nämä ovat instanssikohtaisia
mies.osoite = "Taitoniekantie 9 B 701"
nainen.osoite = "Emännäntie 13"
print(mies.osoite)
print(nainen.osoite)
# metodia kutsuttaessa ensimmäisenä olevaa self-parametriä ei käytetä. Python hoitaa tämän automaattisesti
mies.tulosta()
nainen.tulosta()
Tietorakenteita
Katso esimerkki tietorakenteista
Tiedostojen käsitteleminen
Pythonissa voi käsitellä tiedostoja open-funktiolla, mutta suositeltavampaa on käyttää io-kirjastoa. io-kirjasto osaa paremmin käsitellä erilaiset merkistöt.
import io
f = io.open("test.txt", mode="r", encoding="utf-8")
print(file.read()) #lukee koko tiedoston. Jos haluaa lukea rivi kerrallaan niin f.readline()
f.close()
# rivi kerrallaan voi lukea myös seuraavasti:
for line in f:
print(line)
import io
# seuraava sulkee tiedoston automaattisesti käsittelyn loputtua
with io.open('tiedosto.txt', 'w', encoding="UTF-8") as file:
file.write('Kirjoitetaan tiedostoon')
Serialisointi
Haluttaessa tallentaa ohjelmointikielen muuttujien tai laajempien tietorakenteiden sisältö esim. tiedostoon tai siirrettäväksi verkossa on osattava serialisoida (serialization, Marshalling) ohjelmointikielen sisäinen rakenne käyttökelposeen muotoon.
Yksinkertaisimissa tilanteissa riittää muuntaa haluttu rakenne JSON-muotoon. Voidaan käyttää myös base64-koodausta. Python-kielen oma binääriserialisointiformaatti on pickle
base64-koodattu muoto voi olla hieman tiiviimpi kuin url-koodattu JSON-versio, mutta paljon tähän vaikuttaa millaista rakennetta ollaan tallentamassa. Kannattaa heti miettiä tiivis rakenne.
taulukko = [
[0,1,0,1,0,1,0,1],
[0,1,5,1,0,1,0,1],
[0,1,4,1,0,1,0,1],
[0,1,3,1,0,1,0,1],
[0,1,0,1,2,1,0,1],
[0,1,0,1,7,1,0,1],
[0,1,0,1,10,1,0,1],
[0,1,0,1,0,1,0,1]
]
import base64
import pickle
#serialisoidaan suoraan json-muotoon. Tässä ilman url-koodausta. Tämä soveltuu suoraan lomakkeelle tallennettavaksi
json_versio = json.dumps(taulukko, indent=None, separators=(',',':'))
print("json", len(json_versio), "\n", json_versio)
#serialisoidaan suoraan json-muotoon. Muistetaan url-koodaus. Soveltuu linkkeihin
json_versio = urllib.parse.quote_plus(json.dumps(taulukko, indent=None, separators=(',',':')))
print("json+url-koodaus", len(json_versio), "\n", json_versio)
#json-muotoon serialisoitu rakenne muunnetaan urlsafe_base64-muotoon. Soveltuu linkkeihin
b64 = base64.urlsafe_b64encode( json.dumps(taulukko, indent=None, separators=(',',':')).encode('UTF-8')).decode("ascii")
print("json->base64", len(b64), "\n", b64)
#Serialisoidaan picklellä ja muunnetaan base64-muotoon. Yllättäen on pitempi kuin edellinen versio
b64 = base64.urlsafe_b64encode( pickle.dumps(taulukko)).decode("ascii")
print("pickle->base64", l
Edellä olevan koodin lopputulos:
[tjlahton@charra python]$ ./b64.py
json 146
[[0,1,0,1,0,1,0,1],[0,1,5,1,0,1,0,1],[0,1,4,1,0,1,0,1],[0,1,3,1,0,1,0,1],[0,1,0,1,2,1,0,1],[0,1,0,1,7,1,0,1],[0,1,0,1,10,1,0,1],[0,1,0,1,0,1,0,1]]
json+url-koodaus 308
%5B%5B0%2C1%2C0%2C1%2C0%2C1%2C0%2C1%5D%2C%5B0%2C1%2C5%2C1%2C0%2C1%2C0%2C1%5D%2C%5B0%2C1%2C4%2C1%2C0%2C1%2C0%2C1%5D%2C%5B0%2C1%2C3%2C1%2C0%2C1%2C0%2C1%5D%2C%5B0%2C1%2C0%2C1%2C2%2C1%2C0%2C1%5D%2C%5B0%2C1%2C0%2C1%2C7%2C1%2C0%2C1%5D%2C%5B0%2C1%2C0%2C1%2C10%2C1%2C0%2C1%5D%2C%5B0%2C1%2C0%2C1%2C0%2C1%2C0%2C1%5D%5D
json+base64 196
W1swLDEsMCwxLDAsMSwwLDFdLFswLDEsNSwxLDAsMSwwLDFdLFswLDEsNCwxLDAsMSwwLDFdLFswLDEsMywxLDAsMSwwLDFdLFswLDEsMCwxLDIsMSwwLDFdLFswLDEsMCwxLDcsMSwwLDFdLFswLDEsMCwxLDEwLDEsMCwxXSxbMCwxLDAsMSwwLDEsMCwxXV0=
pickle+base64 236
gANdcQAoXXEBKEsASwFLAEsBSwBLAUsASwFlXXECKEsASwFLBUsBSwBLAUsASwFlXXEDKEsASwFLBEsBSwBLAUsASwFlXXEEKEsASwFLA0sBSwBLAUsASwFlXXEFKEsASwFLAEsBSwJLAUsASwFlXXEGKEsASwFLAEsBSwdLAUsASwFlXXEHKEsASwFLAEsBSwpLAUsASwFlXXEIKEsASwFLAEsBSwBLAUsASwFlZS4=
Esimerkkiohjelmia
Luentomalli 2019 ( lähdekoodi)
Luentovideoita
Python-luento (2019)
Python-luento (2018)
Vanha Python-luento (2016)
Vielä vanhempi Python-luento (2014)
Käyttäjien kommentit