Python
Mureakuha
Python on korkean tason tulkattava olio-ohjelmointikieli. Kieli on vahvasti mutta dynaamisesti tyypitetty. Monipuoliset perustietotyypit yhdessä selkeän ja yksinkertaisen syntaksin kanssa mahdollistavat nopean ohjelmankehityksen.
Ominaisuuksia
- tulkattava
- laaja valikoima perustietotyyppejä, mm.
- luvut (kokonais-, liuku-, kompleksi- ja rajattoman pituuden omaavat pitkät kokonaisluvut)
- merkkijono - sekä ASCII että Unicode
- lista
- dictionary
- olio-ohjelmointi (mm. luokat ja moniperintä)
- koodin ryhmittely moduuleihin ja paketteihin
- poikkeukset
- list comprehensionit
- automaattinen roskienkeruu
- koodilohkot määritellään sisentämällä
- laaja standardikirjasto (mm. WWW-yhteydet ja säännölliset lausekkeet)
- asennuspaketti sisältää dokumentaation (mm. tutoriaali) ja interaktiivisen kehitysympäristön
- laaja tuki eri alustoille (Windows, MacOS, Linux, monet Unixit, OS/2,Series 60, ...)
- avoimen lähdekoodin alainen, vapaasti käytettävissä myös kaupallisiin tarkoituksiin
Asentaminen
Python todennäköisesti tulee Linux-distron mukana, mutta Windowsiin se tarvitsee asentaa erikseen. Ellei erityisen painavaa syytä ole, kannattaa aina asentaa uusin Pythonin kotisivuilla saatavilla oleva versio.
Mahdolliset Pythoniin asennettavat lisämoduulit (esim. WWW-palvelin) ovat aina versiokohtaisia, joten vanhat versiot eivät toimi uuden Python-version kanssa.
Ensiaskeleet
Asennuspaketin mukana tulee interaktiivinen IDLE-käyttöliittymä, johon voi kirjoittaa välittömästi suoritettavia koodirivejä. Vähänkään pidemmät koodipätkät kannattaa kirjoittaa omaan tiedostoonsa, mutta aloittelijalle on helpointa tutustua ensin interaktiiviseen tilaan.
Asennuksen mukana tulevasta tutoriaalista on hyvä aloittaa, varsinkin jos on ennestään ohjelmointikokemusta. Mikäli Python on käyttäjän ensimmäinen ohjelmointikieli, kielen kotisivuilla on linkkejä sopivaan englanninkieliseen oppimateriaaliin (Python for Non-Programmers).
IDLE 1.1 >>> 2 * 5 10 >>> 2 ** 200 1606938044258990275541962092341162602522202993782792835301376L >>> print 'Hello World!' Hello World! >>> 'kas' + 'vain' 'kasvain' >>> 'kas' * 3 'kaskaskas' >>> range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> import random >>> lottorivi = random.sample(range(1, 40), 7) >>> lottorivi [39, 36, 15, 3, 20, 23, 21] >>> lottorivi.sort() >>> print 'Voittava rivi: ' + ' '.join(str(x) for x in lottorivi) Voittava rivi: 3 15 20 21 23 36 39 >>>
Yllä oleva suoritettiin IDLE:n pääikkunassa. Muut oppaan esimerkkikoodit (joissa kehotetta >>> ei ole näkyvissä) on tarkoitus sijoittaa omaan tiedostoonsa ja suorittaa sitä kautta. Se tapahtuu valitsemalla IDLE:n File-valikosta New Window, jolloin avautuvaan ikkunaan koodi voidaan kirjoittaa. Sen suorittaminen tapahtuu painamalla F5.
Kielioppia
Syntaksista
Pythonin syntaksi poikkeaa jonkin verran muista yleisistä kielistä. Koodi on varsin tiivistä verrattuna esim. C-kieleen, mutta se on silti hyvin luettavaa. Ohjelmointia aiemmin harrastanut luultavasti kiinnittää eniten huomiota pakkosisennykseen, eli koodilohkoja ei määritellä esimerkiksi aaltosuluilla vaan ne määräytyvät sisennyksen perusteella. Ratkaisu voi tuntua aluksi erikoiselta, mutta muutenkin tiiviiseen syntaksiin se istuu erittäin hyvin. Kommenttimerkkinä toimii #. Funktion rungon ensimmäisellä rivillä oleva merkkijono tallentuu funktion dokumentaatioksi (käyttö: help(fibonacci) tai print fibonacci.__doc__).
def fibonacci(n): """Palauta Fibonaccin lukujonon n:s alkio""" a, b, x = 1, 1, 2 while x < n: a, b = b, a + b x += 1 return b
Kaksoispistettä käytetään lohkon alkaessa (class, def, for, while, if jne.) ja sen avulla editori ymmärtää sisentää seuraavasta rivistä alkaen. Sisennykset tallennetaan välilyönteinä.
Pythonin syntaksi on lähes yhtä ilmaisuvoimainen kuin Perlin vastaava. Koodaaja voi keskittyä olennaiseen, koska kieli sisältää hyvät perustyökalut kaikkiin tavallisiin operaatioihin. Alla oleva (toiminnaltaan hyvin rajoitettu) esimerkkifunktio muuntaa tekstin morse-koodiksi.
def morse_code(s): morse = { 'o': '---', 's': '...' } return ' '.join([morse[c] for c in s.lower() if c in morse.keys()]) print morse_code('SOS') # ... --- ...
Ylläoleva funktio morse_code
- määrittelee dictionaryn morse-koodille
- muodostaa pienikirjaimisen kopion käyttäjän antamasta merkkijonosta (
s.lower()) - muodostaa listan, joka koostuu merkkijonon morse-muunnoksesta (
['...', '---', '...']) - liittää välilyönneillä yhteen tuon listan alkiot
- palauttaa näin syntyneen merkkijonon
Muuttujista
Korkean tason kielenä Python sisältää valmiiksi monia hyödyllisiä perustietotyyppejä. Otettaessa muuttuja käyttöön sen tyyppiä ei tarvitse ilmoittaa, vaan tulkki päättelee tyypin annettavan arvon perusteella. Esim. a = 1 asettaa a:n arvoksi kokonaisluvun yksi. Tässä luvussa esitellään osa Pythonin perustietotyypeistä.
Pythonissa on käytössä dynaaminen tyypitys, eli vaikka muuttuja on aina jotakin tyyppiä, tuota tyyppiä voidaan vaihtaa ohjelman suorituksen aikana. Tyyppimuunnosfunktioista käytetyimmät ovat int(), float() ja str(). Esim. muuttujan a sisältämästä merkkijonosta '3' saadaan kokonaisluku 3 seuraavasti:
>>> a = '3' >>> a = int(a)
Ennen kattavampaa esittelyä lyhyt silmäys yleisimpiin perustietotyypeihin:
a = 3 # kokonaisluku b = 2.7 # liukuluku c = 3 + 2j # kompleksiluku d = 'erkki' # merkkijono e = [2, 5, 'r'] # lista f = (2, 5, 'r') # monikko eli tuple g = {'b': 2, 'c': 3} # dictionary
Kokonaisluku
Kokonaislukuja käytetään tuttuun tapaan. Pythonissa kokonaisluvuilla ei ole ala- tai ylärajaa, vaan niiden pituutta rajoittaa ainoastaan käytettävissä olevan muistin määrä.
x = 10 y = 5 / 2 # y = 2, ei 2.5 print '2 + 2 = %d' % (2 + 2) # Tulostaa: 2 + 2 = 4 # dynaaminen tyypitys: merkkijonosta kokonaisluvuksi a = '43' # '43' a = int(a) # 43 a % 5 # 3, jakojäännös 5 ** 2 # 25, potenssiin korotus int(5.9) # 5, muuttaa kokonaisluvuksi leikkaamalla desimaalit
Liukuluku
Määrittely tapahtuu desimaalipisteen avulla.
>>> pituus = 1.74 >>> y = 5.0 / 2 # y on nyt 2.5 >>> print '%.3f' % (10.0 / 7) # tulostus 3 desimaalin tarkkuudella 1.429 >>> float('5.21') 5.21 >>> float(2) 2.0
Kompleksiluku
Imaginääriyksikön tunnus on j.
>>> 4 - 6j + 3 * (-1 + 4j) (1+6j) >>> 5j ** 2 (-25+0j)
Merkkijono
Merkkijono voidaan määritellä kolmella tavalla: heittomerkillä, lainausmerkillä tai kolmella lainausmerkillä ympäröitynä.
nimi = 'Jaakko' reknro = "ABC-123" numero = str(123) # '123' lainaus = 'Ville: "Helppoa!"' a = """Rivinvaihtoja sisältävän merkkijonon määrittely"""
Katso alla olevasta Lista-luvusta, miten merkkijono-oliot käyttäytyvät.
Lista
Lista on Pythonissa erittäin hyödyllinen luettelomuotoinen tietorakenne. Se korvaa monesta kielestä tutun taulukko-tyypin, mutta ominaisuuksia on huomattavan paljon (esim. sisäänrakennettu sort()-metodi). Listaan voidaan tallettaa mitä tahansa alkioita, myös toisia listoja.
Alla on esimerkkejä listan käytöstä. Indeksiä käyttämällä saadaan listasta poimittua yksittäisiä alkioita. Negatiivisilla indekseillä laskenta aloitetaan listan lopusta päin eli -1 tarkoittaa listan viimeistä alkiota. Kaksoispisteellä voidaan listan osasta tehdä uusi kopio. Tässä yhteydessä on helpointa ajatella indeksit alkioiden väleihin.
t = [] # aluksi luodaan tyhjä lista t.append('a') # t = ['a'] t.extend(['b', 'c']) # t = ['a', 'b', 'c'] t.append(2352) # t = ['a', 'b', 'c', 2352] t.pop() # palauttaa 2352, nyt t = ['a', 'b', 'c'] # 0 1 2 3 4 5 pos. indeksit listan osalle r = ['a', 'b', 'c', 'd', 'e', 'f'] # -6 -5 -4 -3 -2 -1 neg. indeksit listan osalle r[1] # 'b' r[1:] # ['b', 'c', 'd', 'e', 'f'] r[:-1] # ['a', 'b', 'c', 'd', 'e'] r[-2:] # ['e', 'f'] r[2:-2] # ['c', 'd'] r[:] # helpoin tapa kopioida koko lista
Huom! Lista ja dictionary kuuluvat muokattaviin (mutable) tietotyyppeihin, toisin kuin suurin osa muista perustietotyypeistä. Käytännössä tämä tarkoittaa mm. sitä, että välitettäessä lista funktiolle, joka muokkaa listaa, muutokset välittyvät myös funktion ulkopuolelle. Esimerkit valaisevat asiaa:
def tulosta_jarjestyksessa(s): s.sort() # järjestetään lista print s t = [2, 5, 1] print t # tulostaa: [2, 5, 1] tulosta_jarjestyksessa(t) # tulostaa: [1, 2, 5] print t # tulostaa: [1, 2, 5]
Listasta ei voi ottaa kopiota tyyliin s = t, joka tekee vain uuden viittauksen samaan olioon. Kuten yllä mainittiin, identtisen kopion saa helpoiten s = t[:] -sijoituksella. Seuraava koodi toimii halutulla tavalla:
def tulosta_jarjestyksessa(s): r = s[:] r.sort() # järjestetään lista print r t = [2, 5, 1] print t # tulostaa: [2, 5, 1] tulosta_jarjestyksessa(t) # tulostaa: [1, 2, 5] print t # tulostaa: [2, 5, 1] eli järjestys säilyi
Miksei sama tapahdu esim. merkkijonoilla? Niissäkin kopioidaan viittaus samaan olioon, mutta koska merkkijono on muokkaamaton (immutable), ei ole olemassa tapaa, jolla viittauksen kohteena olevaa merkkijonoa voisi muokata. Täten merkkijonoja muokkaavat funktiot palauttavat uuden merkkijono-olion, joka on muodostettu vanhan pohjalta. Esimerkki:
a = 'Terve' b = a b = b.lower() # b = 'terve', a = 'Terve' b = b.upper() # b = 'TERVE', a = 'Terve' # 'terve'-merkkijono-olioon ei ole enää viittauksia, joten # automaattinen roskienkeruu vapauttaa sen viemän muistin. # Seuraava rivi luo uuden merkkijono-olion 'terve', joka on # olemassa vain print-käskyn suorituksen ajan. a säilyy # muuttumattomana koko ajan, koska sitä käytettiin vain # pohjana luotaessa uusi olio. print a.lower()
Monikko (tuple)
Monikko (engl. tuple) on hieman samanlainen kuin lista, mutta sitä ei voi muokata. Muokkaamattomuudesta johtuen sitä voidaan käyttää dictionaryn avaimena, toisin kuin listaa. Monikko on myös hyvä valinta muuttumattoman datajoukon säilyttämiseen.
suunnat = ((1, 0), (0, 1), (-1, 0), (0, -1)) def naapurit(x, y): return [(x + sx, y + sy) for sx, sy in suunnat] naapurit(4, 7) # Palauttaa: [(5, 7), (4, 8), (3, 7), (4, 6)]
Dictionary
Dictionary (ei vakiintunutta suomenkielistä termiä) on kokoelma avain-arvo-pareja. Avaimen täytyy olla muuttumatonta (engl. immutable) tyyppiä, eli se ei saa olla lista tai toinen dictionary. Toteutuksesta johtuen dictionaryn avainten järjestyksestä ei voida olettaa mitään.
postinro = {'Matti': '00100', 'Liisa': '27300', 'Petteri': '99999'} postinro['Liisa'] # Palauttaa: '27300' postinro.keys() # Palauttaa: ['Petteri', 'Matti', 'Liisa'] del postinro['Matti'] # postinro = {'Petteri': '99999', 'Liisa': '27300'} def kirjaimia(sana): kirjaimet = {} for kirjain in sana: if kirjaimet.has_key(kirjain): kirjaimet[kirjain] += 1 else: kirjaimet[kirjain] = 1 return kirjaimet kirjaimia('hellalla') # Palauttaa: {'a': 2, 'h': 1, 'e': 1, 'l': 4} # Tai sama kätevämmin käyttäen joukkoa ja generaattorilauseketta # (näistä on kerrottu alempana) sana = 'hellalla' dict((letter, sana.count(letter)) for letter in set(sana))
Joukko (set)
Joukko kuuluu Pythonin tietotyyppeihin versiosta 2.4 lähtien. Kuten dictionaryssa, myöskään joukon alkioiden järjestys ei ole määrätty eikä duplikaatteja ole. Tietotyypin voima piilee sen tehokkuudessa muodostaa uusia joukkoja toisista matemaattisten operaatioiden avulla.
Joukko voidaan muodostaa mistä tahansa sarjamuotoisesta datasta set-funktion avulla. Seuraavassa joitakin esimerkkejä:
a = set('allas') # a = set(['a', 's', 'l']) b = set(('k', 'a', 'l', 'l', 'e')) # b = set(['a', 'k', 'e', 'l']) a - b # joukkojen a ja b erotus: set(['s']) b - a # joukkojen b ja a erotus: set(['k', 'e']) a & b # leikkaus (intersection): set(['a', 'l']) a | b # yhdiste (union): set(['a', 's', 'e', 'k', 'l'])
Käytännön esimerkkinä pollataan muutosta työntekijälistassa. Työntekijät tallennetaan tiedostoon ;-merkillä toisistaan erotettuna. Funktio päivittää listan nykyisistä työntekijöistä ja tulostaa mahdolliset muutokset aikaisempaan:
def paivita_tt_lista(tt_tiedosto): ent = set([tt for tt in file(tt_tiedosto, 'r').read().split(';')]) nyk = set(tt_lista()) # palauttaa listan nykyisistä, ei näytetä tässä tulleet = nyk - ent menneet = ent - nyk if len(tulleet): print 'Uudet työntekijät: ' + ', '.join(tulleet) if len(menneet): print 'Lähteneet työntekijät: ' + ', '.join(menneet) file(tt_tiedosto, 'w').write(';'.join(nyk))
Joukon duplikaatittomuusominaisuus on hyvin kätevä erilaisissa algoritmeissa. Sitä voi käyttää näppärästi esimerkiksi selvitettäessä onko annetussa listassa duplikaatteja:
def kaikki_erilaisia(lista): return len(set(lista)) == len(lista) print kaikki_erilaisia([1,2,3,4]) # => True print kaikki_erilaisia([1,1,2,3]) # => False
Funktiot
Pythonissa funktiot määritellään avainsanan def avulla. Määrittelyn ensimmäinen rivi on muotoa
def funktion_nimi(argumenttilista):
jossa argumenttilista sisältää pilkuilla erotetut argumentit toisistaan. Niille voi antaa myös oletusarvoja, esim.
def talo(pinta_ala, kerroksia=1):
Tällöin ylläolevaa funktiota voidaan kutsua joko yhdellä tai kahdella argumentilla.
Joskus on tarpeen tehdä funktioita, joille annettavien argumenttien määrä voi vaihdella. Silloin argumenttilista saadaan talteen tähteä käyttäen:
def keskiarvo(*luvut): return float(sum(luvut)) / len(luvut) print keskiarvo(2, 2, 4, 5) # 3.25
Paluuarvo voi olla mikä tahansa tai se voi myös puuttua kokonaan, jolloin paluuarvoksi jää None.
Vaikka def-lause näyttää hieman erilaiselta, kuin sijoituslause (a = b), ovat Pythonin funktiot kuitenkin olioita - ja def myös eräänlainen sijoituslause - siinä missä Python-maailman datakin. Funktioista voi siis luoda aliaksia vaikka seuraavaan tapaan:
average = keskiarvo print average(2, 2, 4, 5) # 3.25
Todellisen voimansa tämä notaatio osoittaa ns. korkeamman asteen funktioiden kanssa - Pythonissa on nimittäin helppo muodostaa funktioita, jotka palauttavat toisen funktion:
def tee_potenssifunktio(eksponentti): def potenssiinkorotus(kantaluku): # huomaa, kuinka voimme viitata ulomman funktion # argumenttiin. Funktiot sidotaan aina siihen # leksikaaliseen (siis nimi-) ympäristöön, missä # ne on määritelty; tätä sanotaan klosuuriksi. return kantaluku ** eksponentti # palautetaan määritelty funktio return potenssiinkorotus # nyt voimme luoda eri potenssiinkorotusfunktioita näppärästi nelioi = tee_potenssifunktio(2) # ts, f(x) = x^2 kuutioi = tee_potenssifunktio(3) # ts, f(x) = x^3 hyperkuutioi = tee_potenssifunktio(4) # ... # seuraava laskee 5^4. # sisäisesti kutsutaan sellaista potenssiinkorotus-funktiota, # joka muistaa eksponentti-muuttujan arvoksi 4. print hyperkuutioi(5) # tulostaa 625
If-lause
Käskyjen ehdollinen suorittaminen on ohjelmoinnin peruspilareita. Pythonissa perustyökaluna on if-elif-else-kokonaisuus. Tässä esimerkkejä:
if a > 0: print 'Lukusi on positiivinen' elif a < 0: print 'Lukusi on negatiivinen' else: print 'Nollahan tuo' if a % 2: print 'Pariton' else: print 'Parillinen'
For-silmukka
For-silmukan käyttö poikkeaa monien muiden kielten vastaavasta. Pythonissa sillä iteroidaan sarjamuotoista dataa alkio kerrallaan. Merkkijonoa käsitellään kirjain kerrallaan, listaa ja monikkoa alkio kerrallaan jne. Toki perinteinen lukualueen läpikäynti onnistuu myös, esim. range()-funktion avulla.
# Tulostaa kirjaimet a, u, t ja o omille riveilleen: for c in 'auto': print c # Tulostaa luvut 3, 6 ja 9 omille riveilleen: for x in [1, 2, 3]: print 3 * x suom = ['Pohjoinen', 'Etelä'] engl = ['North', 'South'] for i in range(2): print '%d: %s - %s' % (i + 1, suom[i], engl[i]) # Tulostaa: # 1: Pohjoinen - North # 2: Etelä - South
While-silmukka
While toimii yllätyksettömästi: silmukan suoritusta toistetaan niin kauan kuin annettu ehto on voimassa.
def laske_nollaan(n): while n >= 0: print n n -= 1
List comprehension
List comprehension (ei vakiintunutta suomenkielistä termiä) on tiivis ja tehokas tapa muodostaa sarjatyyppisestä muuttujasta (mm. merkkijono, lista tai monikko) uusi lista jonkin operaation avulla. List comprehension on muotoa [... for ... in ...] tai [... for ... in ... if ...], jos halutaan rajoittaa uuteen listaan tulevia alkioita jollain ehdolla.
[c for c in 'auto'] # ['a', 'u', 't', 'o'] [c for c in 'auto' if c != 't'] # ['a', 'u', 'o'] ['X' * n for n in range(1, 5)] # ['X', 'XX', 'XXX', 'XXXX'] def numeroiden_summa(s): return sum([int(x) for x in str(s) if x in '0123456789']) numeroiden_summa('3 ja 4') # Palauttaa 7 numeroiden_summa(127) # Palauttaa 10
Iteraattori
Kuten edellä for-silmukan yhteydessä tuli ilmi, Pythonissa sarjamuotoista dataa voidaan helposti käydä läpi (iteroida) alkio kerrallaan. Tämä tapahtuu nk. iteraattorien avulla.
For-silmukkakin toimii kutsumalla iter-funktiota ja antamalla sille argumenttina sarjamuotoista dataa sisältävän olion. Kyseinen funktio palauttaa iteraattorin. Tämän jälkeen for kutsuu iteraattorin next-metodia silmukan jokaisen suorituskerran alussa niin kauan, kunnes kaikki arvot on käyty läpi.
Iteraattori luodaan "manuaalisesti" seuraavalla tavalla:
>>> s = 'xy' >>> it = iter(s) >>> it <iterator object at 0x00B12930> >>> it.next() 'x' >>> it.next() 'y' >>> it.next() Traceback (most recent call last): File "<pyshell#7>", line 1, in -toplevel- it.next() StopIteration
Ylläoleva esimerkki ei ole sinällään hyödyllinen; se vain havainnollistaa iteraattorien toimintaa kielessä. For-silmukan käyttö peittää yksityiskohdat, eli ylläoleva menisi sen avulla: for a in 'xy':, jolloin a kävisi kaikki arvot ('x' ja 'y') läpi. Iteraattorien idea on kuitenkin syytä ymmärtää, sillä itse luodut iteraattorit ovat joskus erittäin käyttökelpoisia. Tästä on esimerkki alempana.
Generaattori
Generaattorilla voidaan luoda iteraattori. Generaattori määritellään samaan tapaan kuin tavallinen funktio, mutta return-lauseen sijasta käytetään yield-lausetta. Generaattoria kutsumalla saadaan paluuarvona iteraattori. Lyhyesti sanottuna generaattori määrittelee tavan, jolla datajoukko käydään läpi. Generaattori voi määritellä itse datajoukon tai saada sen argumenttina.
Yksi yield-lauseen suorituskerta vastaa iteraattorin next-metodin kutsumista. Erona return-lauseeseen on se, että generaattorin sisäisten muuttujien arvot säilyvät niin kauan, kunnes iteraattorin suoritus päättyy. Käytännöllisenä ja yksinkertaisena esimerkkinä toimikoot Fibonaccin sarjan n:n ensimmäisen alkion iterointi:
def fib(n): a, b = 0, 1 while n > 0: a, b = b, a + b n -= 1 yield a # Tulostaa luvut 1, 1, 2, 3 ja 5 omille riveilleen: for f in fib(5): print f
Jokainen generaattorin kutsu (esim. fib(5), fib(80)) palauttaa itsenäisen iteraattorin, eivätkä mahdolliset samasta generaattorista luodut ja samaan aikaan olemassa olevat iteraattorit sotke toisiaan. Iteraattorilla saadaan käytyä läpi mahdollisesti suurikin datajoukko ilman, että sitä tarvitsee luoda muistiin kerralla. Ylläoleva tapa tulostaa Fibonaccin sarjan alkioita säästää siis muistia huomattavasti verrattuna funktioon, joka loisi ja palauttaisi listan halutuista Fibonaccin alkioista. Viidellä alkiolla ero on merkityksetön, mutta generaattorin avulla voi käydä läpi vaikka miljoonia alkioita, eikä muistia kulu yhtään enempää. No, miljoonannen alkion kohdalla lukujen pituudet olisivat jo aikamoisia.
Generaattorilauseke
Generaattorilauseke (generator expression) on list comprehensionin syntaksia muistuttava (hakasulkeiden sijasta käytetään kaarisulkeita) keino luoda iteraattori. Generaattorilausekkeita kannattaa suosia list comprehensionin sijasta, ellei ole erityistä tarvetta säilyttää koko listaa muistissa.
Käytetään ylempänä määriteltyä Fibonacci-generaattoria ja tulostetaan tuhannen ensimmäisen alkion joukosta ne, jotka ovat jaollisia seitsemällä. Esimerkin koodin muistinkulutus on hyvin vähäistä, sillä se sisältää vain kaksi iteraattoria (fib ja fib_seven), eikä luo yhtään isoa tietorakennetta:
fib_seven = (x for x in fib(1000) if x % 7 == 0) for f in fib_seven: print f
Generaattorilauseke voidaan sijoittaa myös ilman ylimääräisiä sulkuja funktiokutsun sisälle. Tulostetaan edellisen esimerkin alkioiden summa:
print sum(x for x in fib(1000) if x % 7 == 0)
Käytännön esimerkkinä näytetään tarkistusnumeron liittäminen viitenumeron perään:
def viitteen_tarkistusnro(s): return s + str(-sum(int(x)*[7,3,1][i%3] for i, x in enumerate(s[::-1])) % 10) print viitteen_tarkistusnro('1234') # Tulostaa '12344'
Puretaan tuon toiminta auki:
- s[::-1] kääntää merkkijonon '1234' muotoon '4321'
- enumerate-funktio iteroi merkkijonoa indeksien kanssa: (0, '4'), (1, '3'), (2, '2') ja (3, '1')
- [7, 3, 1][i%3]-osa antaa kertoimeksi vuorotellen numeroita 7, 3, 1, 7, 3, 1, 7, 3, 1,...
- sum-funktio laskee summan 7*4 + 3*3 + 1*2 + 7*1 = 46
- -46 % 10 = 4, joten palautetaan merkkijono '1234' + '4' eli '12344'
Ehtolauseke
Ehtolauseke (conditional expression) lisättiin Pythoniin versiosta 2.5 alkaen. Sillä voi korvata monirivisen if-else-rakenteen yksirivisellä lausekkeella. Ehtolauseke voi heikentää koodin luettavuutta, joten sen käyttöä ei pidä liioitella.
Lauseke on muotoa x if a else y. Lausekkeen arvoksi tulee x, jos a on tosi, muuten y. Ehtolauseke evaluoidaan laiskasti, eli a:sta riippuen vain x tai y evaluoidaan.
Lausekkeen lopullisesta syntaksista käytiin vilkasta keskustelua python-devissä ja comp.lang.python-alueella, kunnes kielen luoja Guido van Rossum valitsi nykyisen muodon.
Ehtolauseke:
luokitus = 'pariton' if x % 2 else 'parillinen'
Sama toteutettuna perinteisemmällä if-else-rakenteella:
if x % 2: luokitus = 'pariton' else: luokitus = 'parillinen'
Syöttö ja tulostus
Näppäimistöltä lukeminen tehdään yleisimmin raw_input-funktiolla. Sen paluuarvo on merkkijono, josta viimeinen rivinvaihto on poistettu.
>>> vuosi = int(raw_input('Mikä vuosi nyt on? ')) Mikä vuosi nyt on? 2005 >>> vuosi 2005
Tulostus tapahtuu print-komennolla. Usein käytetään %-operaattoria sisällyttämään tulostukseen muuttujien sisältöä. Yleisimmin käytetään seuraavia: %s = merkkijono, %d = kokonaisluku ja %f = liukuluku. Tulostusta voi muotoilla monella tavalla, samoin kuin esim. C-kielen printf-funktiossa.
>>> a, b = 6, 4 >>> print '%d / %d = %.3f' % (a, b, float(a) / b) 6 / 4 = 1.500 >>> henk = [['Ari', 1952], ['Outi', 1970]] >>> for nimi, synt in henk: ... print '%s syntyi v. %d' % (nimi, synt) ... Ari syntyi v. 1952 Outi syntyi v. 1970
Tiedostojen käsittely
Tiedostoihin päästään käsiksi file-funktion avulla. Se palauttaa tiedosto-objektin, joka sisältää monia hyödyllisiä metodeja, joista käytetyimmät ovat read, readline, readlines, write, writelines ja close. Seuraava esimerkkikoodi poimii IRC-lokista "python"-tekstin sisältävät rivit ja tallettaa ne erilliseen tekstitiedostoon:
loki = file('irc_log.txt', 'r') # avataan lukemista varten tulos = file('python.txt', 'w') # avataan kirjoitusta varten n = 1 for rivi in loki: if 'python' in rivi.lower(): tulos.write('Rivi %d: %s' % (n, rivi)) n += 1 loki.close() tulos.close()
Luokat
Olio-ohjelmoinnilla on suuri merkitys varsinkin laajojen ohjelmistojen toteuttamisessa. Python tarjoaa siihen erinomaiset työkalut, mutta tässä artikkelissa raapaistaan vain hieman pintaa.
Alla olevassa esimerkissä määritellään luokka Suorakulmio. Luokalle voidaan määritellä muodostin (konstruktori, engl. constructor), joka on aina nimeltään __init__ ja joka suoritetaan luotaessa luokasta uusi olio, ilmentymä (engl. instance), esimerkissämme sk. Tässä tapauksessa se tarvitsee kaksi argumenttia; suorakulmion leveyden ja korkeuden. Jokainen luokan jäsenfunktio saa aina automaattisesti self-argumentin, joka on viittaus sillä hetkellä käsiteltävään olioon.
class Suorakulmio: def __init__(self, leveys, korkeus): self.leveys = leveys self.korkeus = korkeus def ala(self): return self.leveys * self.korkeus def piirra(self, merkki): for i in range(self.korkeus): print self.leveys * merkki sk = Suorakulmio(5, 2) print 'Ala =', sk.ala() sk.piirra('X') # Tulostus: # Ala = 10 # XXXXX # XXXXX
Näytetään vielä miten perintä toimii Pythonissa. Luodaan uusi luokka Nelio periyttämällä se Suorakulmiosta. Ainoana muutoksena on yksi muodostimelle annettava argumentti kahden sijaan. Nelion muodostin toimii tässä tapauksena vain välikätenä eli se kutsuu Suorakulmion muodostinta tekemättä itse mitään. Huomaa olion välitys self-muuttujan avulla.
class Nelio(Suorakulmio): def __init__(self, sivu): Suorakulmio.__init__(self, sivu, sivu) nel = Nelio(3) print 'Ala =', nel.ala() nel.piirra('O') # Tulostus: # Ala = 9 # OOO # OOO # OOO
Poikkeukset
Aloitteleva Python-koodaaja törmää todennäköisesti hyvin pian ensimmäiseen virhetilanteeseen, joka näkyy käsittelemättömänä poikkeuksena. Koodissa ollut huolimattomuusvirhe a = float('3,4') saa tulkin keskeyttämään suorituksen ja tulostamaan virheilmoituksen:
Traceback (most recent call last): File "C:\py\esim.py", line 1, in ? a = float('3,4') ValueError: invalid literal for float(): 3,4
Virhetilanteita hallitaan try-except-rakenteella seuraavaan tapaan:
try: f = open('versiotiedot.txt') versio = int(f.readline().strip()) print 'Versionumero:', versio except IOError, (nro, viesti): print "I/O-virhe versiotiedostoa luettaessa (%s): %s" % (nro, viesti) except ValueError: print "Versiotiedoston ensimmäinen rivi on virheellinen" except: print "Odottamaton virhe versiotiedostoa luettaessa"
Virhetilanteen mahdollisesti aiheuttava koodi sijoitetaan try-lohkon sisään. Jos sen aikana syntyy poikkeus, sitä yritetään käsitellä seuraavissa except-lohkoissa. except-rivillä voidaan määritellä erikseen, mikä tai mitkä poikkeukset halutaan ottaa kiinni. Pelkkä except ottaa kaikki poikkeukset kiinni, ja sitä käytetään usein viimeisenä varmistuksena.
Säikeet
Säikeitä (engl. thread) käytetään suorittamaan useita rinnakkaisia toimintoja yksittäisen ohjelman sisällä. Esimerkiksi internetistä voi hakea monta sivua samaan aikaan, jos haut käynnistetään eri säikeissä.
Seuraavassa on lyhyt esimerkki säikeistä ja niiden keskinäisestä kommunikoinnista:
# -*- coding: latin1 -*- import threading from time import sleep, time def tulosta(s): print '%.f s - %s' % (time() - alkuhetki, s) def silmukka(i, event): while not event.isSet(): tulosta('Silmukka nro %d' % i) sleep(2) tulosta('Silmukka nro %d lopettaa' % i) # Event muille säikeille kommunikointia varten event = threading.Event() alkuhetki = time() tulosta('Alku') th1 = threading.Thread(target=silmukka, args=(1, event)) th1.start() sleep(1) th2 = threading.Thread(target=silmukka, args=(2, event)) th2.start() # Annetaan luotujen säikeiden juosta hetki yksinään sleep(7) # Lopetusviesti säikeille eventin avulla event.set() tulosta('Lopetusviesti') # Odotetaan kunnes molemmat luodut säikeet ovat lopettaneet th1.join() th2.join() tulosta('Valmis')
Tulostus:
0 s - Alku 0 s - Silmukka nro 1 1 s - Silmukka nro 2 2 s - Silmukka nro 1 3 s - Silmukka nro 2 4 s - Silmukka nro 1 5 s - Silmukka nro 2 6 s - Silmukka nro 1 7 s - Silmukka nro 2 8 s - Lopetusviesti 8 s - Silmukka nro 1 lopettaa 9 s - Silmukka nro 2 lopettaa 9 s - Valmis
Automaattinen roskienkeruu
Pythonin automaattinen roskienkeruu auttaa ohjelmoijaa muistinkäytön hallinnassa. Python pitää kirjaa yksittäiseen objektiin kohdistuvista viittauksista ja jossain vaiheessa, kun objektiin ei ole enää yhtäkään viittausta (eli siihen ei voi enää päästä koodissa käsiksi mitenkään), objektin viemä muisti vapautetaan. Otetaan esimerkki:
def laske(a, b): summa = a + b tulos = '%d + %d = %d' % (a, b, summa) return tulos lasku_str = laske(2, 5) print lasku_str # Tulostaa: 2 + 5 = 7 lasku_str = ''
Ylläolevan esimerkin funktiossa laske luodaan kaksi uutta muuttujaa: summa ja tulos. Funktiosta poistumisen jälkeen summaan ei enää ole viittauksia, joten sen viemä muisti asetetaan vapautettavaksi (itse vapautus tapahtuu sitten, kun Python sen päättää tehdä). Muuttujan tulos suhteen tilanne on kuitenkin erilainen. Koska se - tai oikeammin merkkijono-objektiin kohdistuva viittaus - palautetaan funktion ulkopuolelle (lasku_str), sitä voidaan käyttää myöhemminkin. Vasta kun lasku_str asetetaan viittaamaan tyhjään merkkijonoon esimerkin viimeisellä rivillä, aiemmin viittauksen kohteena olleen merkkijonon muistialue voidaan vapauttaa muuhun käyttöön.
