Python-kielen ja CGI-ohjelmoinnin perusteita
- Esimerkkiohjelmia
- Luentovideoita
- Python
- Python
- Poikkeukset
- Sanakirja (dict)
- Monikot (tuple)
- Joukko (set)
- Luokat
- Tietorakenteita
- Tiedostojen käsitteleminen
- CGI-ohjelmointi Pythonilla
- Python ja merkistöt
- Tiedostojen käsitteleminen ja JSON
- Querystring
Esimerkkiohjelmia
Luentomalli 2019 ( lähdekoodi)
Luentovideoita
Python-luento (2018)
Vanha Python-luento (2016)
Vielä vanhempi Python-luento (2014)
Python
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 (v2.7 eli sama versio mitä on käytettävissä users.jyu.fi-koneessa ja halava/jalava-koneissa)
- Pythonin perusteita (TIM)
- Python ohjelmointiopas (PDF)
- Programming python
- Python 2.7 documentation Sama versio mitä on users.jyu.fi:ssä
- Python FAQ
- PEP 0008 - Style Guide for Python Code
Seuraavassa on käyty läpi tärkeimpiä Pythonin piirteitä. Lähteenä on käytetty pääasiassa Jussi Kasurisen Python ohjelmointiopasta ja Python tutoriaali.
- Pythonia voi kokeilla halava.cc.jyu.fi:ssä komentoriviltä kirjoittamalla: python
Pythonin objektien ominaisuuksia ja avustusta voi lukea kirjoittamalla help ja help(objekti)
Python 2.6.6 (r266:84292, May 22 2015, 08:34:51) [GCC 4.4.7 20120313 (Red Hat 4.4.7-15)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> print "hello world!" hello world! >>> quit()
- Suoritettavan python-ohjelman voi tehdä, kun määrää ohjelmakooditiedoston shebang-rivin viittaamaan python-tulkkiin
ja asettaa tiedostolle suoritusoikeudet.
Ohjelmakoodi:
#!/usr/bin/python # -*- coding: utf-8 -*- print "Hello world!"
Yleensä kannattaa myös määritellä ohjelmakoodissa käytetty # -*- coding: utf-8 -*-
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.
- 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 esim. neljällä välilyönnillä
- Suositus on sisentää neljä merkkiä kerrallaan
- Ä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]) converts x to an integer
- float(x) converts x to a floating-point number
- str(x) converts x to a bytestring representation
- unicode(x) converts x to a unicode representation
Merkkijonot
Python 2.7:ssa on hämäävästi kahta eri tyyppiä olevia merkkijonoja: str ja unicode. Käytä aina unicode-tyyppiä. str-tyyppi tarkoittaa oikeasti tavujonoja (bytes) eikä merkkijonoja ja sen käyttäminen aiheuttaa vain ongelmia.
j = "tämä on bytestring" # bytestring k = u"tämä on unicode-merkkijono" #unicode, käytä aina tätä
Jatkossa, kun tässä dokumentissa käytetään sanaa merkkijono, niin tarkoitetaan unicode-merkkijonoa
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.
print u'kukku' u'luuruu'
tai voi käyttää +-operaattoriaprint u'kukku' + u'luuruu'
- 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] = u"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 rakenn 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] = [u'kukku',u'luuruu'] # [3, 9, 3, u'kukku', u'luuruu', u'foo', u'bar']
Yhteisiä funktioita merkkijonoille ja listoille:
- x in s True if an item of s is equal to x, else False (1)
- x not in s False if an item of s is equal to x, else True (1)
- s + t the concatenation of s and t (6)
- s[i] ith item of s, origin 0 (3)
- s[i:j] slice of s from i to j (3)(4)
- s[i:j:k] slice of s from i to j with step k (3)(5)
- len(s) length of s
- min(s) smallest item of s
- max(s) largest item of s
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 u"a on suurempi kuin 0" elif a < 0: print u"a on pienempi kuin 0" else: print u"a on 0"
Toistorakenteet
laskuri = 12 while laskuri> 0: laskuri = laskuri - 1 a = [1,2,3,u'foo',u'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. Perlin 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.
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
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/python
# -*- coding: utf-8 -*-
try:
if int(u"a"):
print "onpas kummaa. a oli kokonaisluku"
#nappaa kiinni kaikki mahdolliset virheet
except:
print u"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(u"a"): # tulee virhe koska Int ei ole olemassa
print u"onpas kummaa. a oli kokonaisluku"
#nappaa kiinni kaikki mahdolliset virheet
except:
print u"ei ollunna integer"
# parempi napata kiinni vain se olennainen virhe
try:
if int(u"a"):
print u"onpas kummaa. a oli kokonaisluku"
except ValueError:
print u"ei ollunna integer"
# nyt pitäisi toimia tulkin oma virheilmoitus
try:
if Int(u"a"):
print u"onpas kummaa. a oli kokonaisluku"
except ValueError:
print u"ei ollunna integer"
kts. Idioms and Anti-Idioms in Python
Sanakirja (dict)
Pythonissa on myös erittäin kätevä tietotyyppi dict eli sanakirja, jota muissa ohjelmointikielissä kutsutaan mm. assosiatiiviseksi taulukoksi. dict-tietotyyppi on siis järjestämätön taulukko, jossa alkioihin viitataan uniikeilla avaimilla.
>>> testi = dict([[1,2], [2,2], [u'kolme',3], [u'neljä',4]])
>>> testi
{u'nelj\xe4': 4, 1: 2, 2: 2, u'kolme': 3}
>>> for x in testi.keys(): print x
...
neljä
1
2
kolme
>>> for x in testi.keys(): print testi[x]
...
4
2
2
3
>>> 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,u"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 = [u'tommi', u'antti', u'ville', u'tommi'] >>> joukko = set(lista) >>> joukko set([u'ville', u'antti', u'tommi']) >>> u'tommi' in joukko True >>> u'kalle' in joukko False >>> joukko2 = set([u'joonas',u'antti',u'kalle']) >>> joukko2 set([u'joonas', u'kalle', u'antti']) >>> joukko - joukko2 #henkilöt joukosta jotka eivät ole joukko2:ssa set([u'ville', u'tommi']) >>> joukko & joukko2 #henkilöt jotka ovat molemmissa joukoissa set([u'antti']) >>> joukko ^ joukko2 #henkilöt jotka ovat jommassa kummassa joukossa mutta eivät molemmissa set([u'joonas', u'kalle', u'tommi', u'ville']) >>> joukko | joukko2 #henkilöt jotka ovat jommassa kummassa joukossa set([u'ville', u'joonas', u'kalle', u'antti', u'tommi'])
Luokat
Luokat toimivat kuten muissakin kielissä. Muutama erikoisuus on syytä huomioida. Lue Class tutorial
#!/usr/bin/python
# -*- 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 = u"Kalle"
tiedot = []
def __init__(self, sukupuoli=u"Nainen"):
self.data = [sukupuoli]
def tulosta(self):
# on käytettävä self
print self.nimi
print self.tiedot
print self.data
mies = Ihminen(u"Mies")
nainen = Ihminen()
mies.nimi = u"Tommi"
mies.tiedot.append(u"180 cm")
mies.tiedot.append(u"75 kg")
print mies.nimi
nainen.nimi = u"Hellu"
print nainen.nimi
print mies.nimi
# molemmilla on samat tiedot!
print mies.tiedot
print nainen.tiedot
nainen.tiedot.append(u"170 cm")
nainen.tiedot.append(u"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(u"30 v")
nainen.data.append(u"35 v")
print mies.data
print nainen.data
# voidaan myös lennosta keksiä uusia ominaisuuksia vrt. Javascript
# nämä ovat instanssikohtaisia
mies.osoite = u"Taitoniekantie 9 B 701"
nainen.osoite = u"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(u'Kirjoitetaan tiedostoon')
CGI-ohjelmointi Pythonilla
CGI eli Common Gateway Interface on yksinkertaisin rajapinta WWW-sovelluksien tekemiseen
HTML-dokumentti Python-kielellä ja CGI-rajapinnan kautta tuotettuna:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import cgitb
cgitb.enable()
print u"""Content-type: text/html; charset=UTF-8
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Malli</title>
</head>
<body>
<h1>Tulostetaan Pythonilla:</h1>
<p>
""".encode("UTF-8")
print u"Hello World! Osaatkönä skändejä?".encode("UTF-8")
print u"""
</p>
</body>
</html>
""".encode("UTF-8")
- #!/usr/bin/python kertoo mistä www-palvelimen pitää etsiä käytettävää komentotulkkia eli tässä tapauksessa pythonia
- # -*- coding: utf-8 -*- varmistaa, että lähdekoodi käsitellään UTF-8-merkistönä eikä vahingossakaan esim. ISO-8859-1-muotoisena
- print u"""Content-type: text/html; charset=UTF-8 Tulostetaan useampi rivi tekstiä jolloin pythonissa käytetään """-merkintää osoittamaan
tulostuksen alku.
Ensimmäiselle riville pitää tulostaa www-selaimen tarvitsema tieto dokumentin mediatyypistä HTTP-otsakkeena (text/html). HTTP-otsakkeen yhteydessä voidaan myös asettaa käytetty merkistö.
Muitakin HTTP-protokollan otsakkeita voitaisiin tulostaa tässä vaiheessa. Varsinainen HTML-dokumentti alkaa ensimmäisen tyhjän rivin jälkeen.
- """ Monen rivin tuloste loppuu
- print u"Hello World!" Tavallinen yhden rivin tulostus
- Muista käyttää kaikissa merkkijonoissa u-etuliitettä ja muista enkoodata tulostettava teksti oikeaan merkistöön esim. print u"Hello World!".encode("UTF-8")
Virheilmoitukset ja asetukset
Pythonilla tehtyjen CGI-ohjelmien debuggaamista helpottaa cgitb-moduulin käyttöönotto:
import cgitb cgitb.enable()
cgitb aiheuttaa virheraporttien näyttämisen www-selaimessa tai niiden tallentamisen tekstitiedostoon.
Oletuksena cgitb-kirjasto tulostaa virheilmoitukset HTML-muodossa. Jos haluat tekstimuotoisen tulostuksen niin käytä kutsua:
cgitb.enable(format="text")
Lisätietoa: HOWTO Use Python in the web
CGI-ohjelmat users.jyu.fi-palvelimella
Viimeisimmät tiedot löytyvät aina digipalvelujen ohjeesta: CGI/SSI-tekniikoiden käyttäminen www-palveluissa (users.jyu.fi/groups.jyu.fi)
- CGI-ohjelmien on oltava W:\cgi-bin\-kansiossa tai sen alikansiossa. Mikään muu kansio ei kelpaa
- CGI-ohjelmien tiedostopääte on oltava .cgi
- Hakemiston (jossa ajettava tiedosto on) omistajan/ryhmän pitää olla samat kuin itse käyttäjän (uid/gid). Nämä tiedot saa selville id-komennolla.
- Suoritettavan tiedoston omistajan ja ryhmän pitää olla samat kuin käyttäjän, jona tiedosto suoritetaan.
- Suoritettavaan tiedostoon tai hakemistoon ei saa olla kirjoitusoikeutta muilla kuin itse käyttäjällä.
- Harvinaisissa tapauksessa SELinux voi aiheuttaa ongelmia. Toimi silloin digipalvelujen ohjeen mukaan.
Python ja merkistöt
- Sinun on aina tiedettävä mikä merkistö on merkkijonoissa käytössä. Tämä koskee kaikkia ohjelmointikieliä
- Kirjoita ohjelmakoodi ja sen merkkijonot UTF-8-merkistössä
# -*- coding: utf-8 -*-
- Kirjoita kaikki ohjelmakoodissa olevat merkkijonot unicode-merkkijonoina:
merkit = u"tämä on unicode-merkkijono"
- html-dokumentin merkistön on myös oltava UTF-8 (content-type, mahdollinen xml-deklaraatio. mahdollinen meta-elementti, formin accept-charset)
HTTP-otsakkeessa oikea merkistö:
Content-type: text/html; charset=UTF-8
Pelkässä tekstimuodossa:
Content-type: text/plain; charset=UTF-8
Edelläolevan pitäisi riittää mutta jos merkistö on mainittu jossain muuallakin niin myös siellä UTF-8:
<!doctype html> <html> <head> <meta charset="UTF-8" /> <title>Malli</title> </head> <body> <form action="" method="post" accept-encoding="UTF-8"> </form> </body> </html>
Tiedostojen käsitteleminen ja JSON
Pythonilla on helppo käsitellä JSON-muotoista tietoa. Pythonin omia tietorakenteita on myös helppo muuntaa JSON-muotoon. JSON-tiedostomuotoa on käsitelty enemmän TIEA2120-kurssilla.
- Lataa käyttöösi simplejson-kirjasto:
import simplejson as json
Nyt voit dumpata haluamasi python-tietorakenteen json-muotoon seuraavasti:print json.dumps(data)
Samaan tapaan voi JSON-muotoisen tietorakenteen muuntaa python-tietorakenteeksi:
import io tiedosto = io.open("data.json", encoding="UTF-8") data = json.load(tiedosto)Pidä huoli, että käytät koko ajan UTF-8-merkistöä
- json.dump osaa suoraan tallentaa python-tietorakenteen tiedostoon.
- JSON-dataa voi ladata myös suoraan verkosta:
sivu = urllib2.urlopen("tähän sivun osoite")
Querystring
- Sivun osoitteeseen voidaan lisätä parametreja seuraavalla tavalla:
http://osoite.example?parametri=arvo¶metri2=arvo1¶metri2=arvo2 http://osoite.example?nimi=M%C3%A4lli%20Henkil%C3%B6&arvosana=5&arvosana=3&arvosana=2&kurssi=Helppo+kurssi&kurssi=Vaikea+kurssi&kurssi=%C3%84ll%C3%B6+kurssi
Osoitteen perään lisätään ?-merkki. ?-merkin jälkeen luetellaan avaimia ja arvoja &-merkillä eroteltuina
Osoitteessa esiintyvät erikoismerkit on koodattava Percent-encoding-tavalla. Pythonissa tämän voi tehdä urllib.quote_plus-funktiolla.
Jos query_stringin sisältävän osoitteen sijoittaa html-dokumenttiin on muistettava koodata myös &-merkit cgi.escape-funktiolla.
- Querystringissä annettuihin parametreihin pääsee käsiksi.
Pythonin cgi-kirjaston avulla. WWW-sivuilla
olevien lomakkeiden lähettämät tiedot käsitellään samalla tavalla. Tähän palataan seuraavien viikkojen tehtävissä.
import cgi fields = cgi.FieldStorage() # alustaa lomake-olion #pyytää ensimmäisen url-nimisen lomakekentän arvon. Jos sitä ei ole annettu käyttää #oletusmerkkijonoa "1". Dekoodaus on ehdottomasti muistettava! Merkistö on yleensä UTF-8, mutta ei aina #Tiedot ovat aina merkkijonoja paitsi oletusarvoksi voi antaa myös muita tyyppejä nimi = fields.getfirst(u"nimi", u"Tuntematon").decode("UTF-8") # Jos samalla parametrilla on useampia arvoja niin ne voidaan pyytää listana # Lista on tyhjä, jos pyydettyä parametria ei löydy # Listan sisältö pitää erikseen muistaa dekoodata! arvosanat = fields.getlist(u"lkm")
Decode In Encode Out
- Decode In
- lomakkeelta tai querystringissä tulevat kentät pitää dekoodata unicode-merkkijonoiksi:
form.getfirst("kentta").decode("UTF-8") - Sama tiedostojen kanssa:
tiedosto = io.open("tietorakenne.json", encoding="UTF-8")
- lomakkeelta tai querystringissä tulevat kentät pitää dekoodata unicode-merkkijonoiksi:
- Encode Out
- Tulostus on enkoodattava UTF-merkistöön. Komentorivillä Python enkoodaa automaattisesti shellin käyttämään merkistöön. Varmista, että sinulla on pääteohjelmassa käytössä UTF-8.
- Lopullinen tulostettava (webbipalvelimen palauttama) merkkijono on enkoodattava UTF-8-merkistöön
print teksti.encode("UTF-8") - Samoin tiedostoon tallennettaessa
tiedosto = io.open("tietorakenne.json", mode="w", encoding="UTF-8")

Käyttäjien kommentit