Ohjelmoinnin peruskurssi Y2, kurssimateriaali

2.1. Pythonin perustyyppien tehokasta käyttöä

«  2. Kierros 2 (29.1.2016 kello 23:59)   ::   Etusivulle   ::   2.2. Pythonin poikkeukset  »

2.1. Pythonin perustyyppien tehokasta käyttöä

Tästä sivusta:

Pääkysymyksiä: Miten hyödyntää Pythonin tarjoamia valmiita tyyppejä tehokkaasti?

Mitä käsitellään? Lists, tuples, sets, dicts, strings; comprehensions.

Suuntaa antava työläysarvio:? 2-3 tuntia.

Esitietokurssilla CSE-A1111 Ohjelmoinnin peruskurssi Y1 käsiteltiin jonkin verran listoja, merkkijonoja ja sanakirjoja. Jos perusteet ovat päässeet unohtumaan, voi käydä läpi Y1-kurssin kurssimonisteen luvun 5: "Listat, merkkijonot ja sanakirja".

Tässä luvussa perehdymme hieman tarkemmin näiden valmiiden tyyppien käyttöön. Tavoitteena on, että opittaisiin sujuvasti käyttämään Pythonille ominaisia mekanismeja. Ulkoisena lähdemateriaalina on

Nämä lähtee kannattaa pitää mielessä ohjelmointitehtäviä ja omaa projektia tehtäessä.

Lista

Listoille löytyy valmiina koko joukko hyödyllisiä operaatioita, joita esitellään tutorialin luvussa 5.1. More on Lists sekä The Python Standard Libraryn dokumentaation luvussa 4.6 Sequence Types.

Alla on muutama esimerkki listan käsittelystä.

Kokeile itse!

Parhaiten nämä asiat selviävät, kun samalla itse kokeilet niitä Python-tulkissa.

Esimerkki: Listojen luominen.

l0 = [] # Using a pair of square brackets to denote the empty list

l1 = [3, 8, 7] # Using square brackets, separating items with commas

l2 = [['a', 1], 'b'] # The same as above, but contains elements of different types and one sublist

l3 = [ i for i in range(2,20,2) ] # Using a list comprehension

l4 = list(range(10)) # Using the type constructor: list() or list(iterable)

r = range(10) # This is an iterable, not a list

Listojen samuus

Kaksi listaa ovat samoja (eli l1 == l2), jos niissä on samat alkiot samassa järjestyksessä (ks. 6.9. Comparisons).

Esimerkki: listojen samuus

l0 == [] # True

l1 == [8, 7, 3] # False

l1 == [3, 8, 7] # True

Sekvenssityyppien yhteiset operaatiot

Lista on esimerkki sekvenssityypistä. Muita ovat mm. monikot (engl. tuple), range ja str.

Kaikilla sekvenssityypeillä on seuraavat operaatiot (ks. 4.6.1. Common Sequence Operations.).

Operaatio Kuvaus
x in s Sisältääkö sekvenssi s alkion x?
x not in s Edellisen negaatio
s + t Sekvenssi jossa on s:n ja t:n alkiot peräkkäin
s * n ja n * s Sekvenssi, jossa on n (matalaa) kopiota s:stä peräkkäin.
s[i] Sekvenssin s i:s alkio (indeksointi alkaa nollasta)
s[i:j] Sekvenssin s alkiot indeksiväliltä i ja j.
s[i:j:k] Sekvenssin s alkiot indeksiväliltä i ja j k:n alkion välein.
len(s) Sekvenssin s pituus
min(s) Pienin alkio
max(s) Suurin alkio
s.index(x) Ensimmäinen indeksi, josta x löytyy s:ssä.
s.index(x, i) Sama, mutta aloitetaan etsintä indeksistä i
s.index(x, i, j) Sama, mutta aloitetaan päätetään indeksiä j ennen.
s.count(x) Kuinka monta kertaa x esiintyy s:ssä.

Esimerkkejä näiden käytöstä:

s = [ -9, 5, 23, 6, 2, 10, 5, 6, -1, 100 ]
t = list(range(4))
x = 2
x in s                     # True
x not in s                 # False
-10 in list(range(2))      # False
10 not in list(range(100)) # False
s + t                      # [-9, 5, 23, 6, 2, 10, 5, 6, -1, 100, 0, 1, 2, 3]
t * 5                      # [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]
22 * [ 0 ]                 # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
s[2]                       # 23
s[20]                      # IndexError: list index out of range
(t*5)[2:10]                # [2, 3, 0, 1, 2, 3, 0, 1]
(t*5)[2:20:3]              # [2, 1, 0, 3, 2, 1]
([3, 5, 1]*2).index(5)     # 1
([3, 5, 1]*2).index(5, 2)  # 4
([1, 2]*100).count(1)      # 100
[1, 2, 3].count(4)         # 0

Muuttuvat ja muuttumattomat sekvenssit

Sekvenssityypeistä osa on muuttuvia (mutable) ja osa muuttumattomia (immutable). Lista on muuttuva tyyppi.

Kaikilla muuttuvilla sekvenssityypeillä on yllä kuvattujen lisäksi seuraavat operaatiot:

Operaatio Kuvaus
s[i] = x i:s alkio korvataan x:llä
s[i:j] = t alkiot välillä i ja j korvataan iterable t:n alkioilla
s[i:j:k] = t sama, mutta i:n ja j:n välillä askelella k
del s[i:j] alkiot välillä i ja j poistetaan
del s[i:j:k] sama, mutta i:n ja j:n välillä askelella k
s.append(x) lisätään x s:n loppuun (sama kuin s[len(s):len(s)] = [x])
s.clear() poistetaan kaikki alkiot s:stä (sama kuin del s[:]) (5)
s.copy() matala kopio s:stä (sama kuin s[:])
s.extend(t) lisätään s:n loppuun t:n alkiot (sama kuin s[len(s):len(s)] = t)
s.insert(i, x) lisätään x s:ään indeksiin i (sama kuin s[i:i] = [x])
s.pop([i]) poistaa ja palauttaa s:n alkion indeksissä i
s.remove(x) poistaa ensimmäisen s:n alkion x
s.reverse() kääntää s:n alkiot edestakaiseen järjestykseen

Sort: Listan alkioiden järjestäminen

Usein on tarpeen saada järjestettyä joukko arvoja. Tämä onnistuu helposti laittamalla ne listaan ja käyttämällä metodia sort.

Operaatio Kuvaus
l.sort(key=None, reverse=None) Järjestää listan alkiot kasvavaan järjestykseen vertaillen niitä < operaatiolla. Jos reverse = True on järjestys laskeva. Parametrilla key voi antaa funktion, joka poimii vertailtavan arvon alkiosta.

Esimerkki sortin käytöstä:

>>> l = [6, 3, 5, 8, 9, 1]
>>> l.sort()
>>> l
[1, 3, 5, 6, 8, 9]
>>> l2 = [('a', 7), ('d', 1), ('x', 3)]
>>> l2.sort(key=lambda e: e[1])
>>> l2
[('d', 1), ('x', 3), ('a', 7)]

List comprehension

Listoja on usein näppärä käsitellä niin mekanismilla nimeltä list comprehension. Listan voi tämän avulla muodostaa soveltamalla valintaa ja laskutoimituksia jonkun toisen listan tai iterablen alkioihin.

Katsotaan esimerkkiä:

[i**2 for i in range(10) if i % 2 == 0]
range(10) on syötteenä käytettävä lista tai iterable
Muuttuja i sidotaan vuorotellen syötteen alkioihin
Valintalauseke if i % 2 == 0, joka valitsee syötteestä halutut alkiot (tämän voi jättää pois)
Tuloslauseke i**2, joka tuottaa syötteestä valitusta alkiota halutun lopputuloksen.

Comprehension voi sisältää myös useamman sisäkkäisen for-rakenteen

>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

Monikko eli tuple

Monikko on muuten sama kuin lista, mutta se on muuttumaton. Sillä on siis kaikki muuttumattomien sekvenssien operaatiot, mutta ei muuttuvien sekvenssien operaatioita eikä myöskään listan sort-operaatiota (sehän muuttaa listan sisältöä).

Monikon luominen

Monikkoja luodaan hyvin samaan tapaan kuin listoja, mutta [] -merkinnän tilalta käytetään () -merkintää ja list() -merkinnän tilalta tuple()-merkintää.

t0 = () # Using a pair of square brackets to denote the empty tuple

t1 = (3, 8, 7) # Using square brackets, separating items with commas

t2 = (['a', 1], 'b') # The same as above, but contains elements of different types (one is a list, which is mutable)

t3 = tuple(range(10)) # Using the type constructor: tuple() or tuple(iterable)

Generaattorilauseke

Pythonissa on merkintä, joka näyttää list comprehensionilta, mutta syntaksissa []-merkinnän tilalta ().

x = (i for i in range(2,20,2) if i % 3 == 0)

Tämä ei kuitenkaan tuota monikkoa, vaan on generaattorilauseke.

Range.

Aiemmin peruskurssilla on jo käytetty rangea for-silmukassa ja yllä käytimme sitä comprehensionissa. Mikä tämä range oikein on?

Range on muuttumaton tasavälinen sekvenssi numeroita laskevassa tai nousevassa järjestyksessä. Se poikkeaa kuitenkin samansisältöisestä tuplesta siinä, että sekvenssin numeroita ei lasketa heti ja kerralla vaan niitä tuotetaan tarpeen mukaan. Tämä on usein huomattavan tehokasta.

Rangella on alaraja, yläraja sekä askel. Rangen voi luoda kolmella tavalla

Operaatio Kuvaus
range(ylä) Numerot väliltä 0 ... ylä-1
range(ala,ylä) Numerot väliltä ala ... ylä-1 (ala <= ylä) tai väliltä ala ... ylä-1 (ylä < ala)
range(ala,ylä,askel) Kuin yllä, mutta askelen välein ylös tai alas

Rangen alkioista saa listan tai tuplen kietaisemalla ympärille list() tai tuple() -konstruktorin.

Esimerkkejä rangesta:

>>> r=range(10)
>>> r
range(0, 10)
>>> list(r)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(3, 15))
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> list(range(20, 1, -3))
[20, 17, 14, 11, 8, 5, 2]

Joukot: set ja frozenset

Joukko on järjestämätön kokoelma alkioita, jossa kukin alkio esiintyy vain kerran. Tätä varten Python tarjoaa kaksi valmista tyyppiä set ja frozenset. Edellinen on muuttuva ja jälkimmäinen muuttumaton tyyppi. Voidaan ajatella, että niiden suhde toisiinsa on vastaava kuin tyyppien list ja tuple. Alkioita vertaillaan == operaatiolla, eli joukon millekään alkioparille a ja b ei päde a == b.

Joukkoja voi luoda seuraavasti:

>>> s={32, 55, 7, 32, 11, 8}
>>> s
{8, 32, 11, 55, 7}
>>> set([9, 11, 2, 11, 9])
{9, 2, 11}
>>> set('joukko merkkijonon merkeistä')
{'j', 'n', 'e', 'ä', 's', 't', 'i', 'm', 'r', 'u', 'k', 'o', ' '}

Huomaa, että merkintä {} ei luo tyhjää joukkoa vaan tyhjän dict-olion.

Joukkotyyppien yhteiset operaatiot:

Operaatio Kuvaus
len(s) Joukon alkioiden määrä
x in s Sisältyykö x s:ään
x not in s Edellisen negaatio
s1.isdisjoint(s2) Tosi, joss joukoilla s1 ja s2 ei yhteisiä alkioita
s1.issubset(s2) Onko joukko s1 joukon s2 osajoukko
s1 <= s2 Sama
set < other Onko joukko s1 joukon s2 aito osajoukko (ei siis s1 == s2)
s1.issuperset(s2) Onko joukko s2 joukon s1 osajoukko
s1 >= s2 Sama
s1 > s2 Onko joukko s2 joukon s1 aito osajoukko (ei siis s1 == s2)
s1.union(s1, ...) Joukkojen union
s1 | s2 | ... Sama
s1.intersection(s2, ...) Joukkojen leikkaus
s1 & s2 & ... Sama
s1.difference(s2, ...) S1:n alkiot, jotka eivät sisälly s2:een ...
s1 - s2 - ... Sama
s1.symmetric_difference(s2) Alkiot, jotka sisältyvät vain s1:een tai vain s2:een
s1 ^ s2 Sama
s.copy() Matala kopio s:stä

Muuttuville joukoille tyyppiä set on lisäksi seuraavat operaatiot:

Operaatio Kuvaus
s1.update(s2, ...) Lisää joukkoon s1 alkiot joukoista s2 ..
s1 |= s2 | ... Sama
s1.intersection_update(s2, ...) Päivitä s1 sisältämään s1:n, s2:n ... leikkaus
s1 &= s2 & ... Sama
s1.difference_update(s2, ...) Päivitä s1 sisältämään s1:n, s2:n ... erotus
s1 -= s2 | ... Sama
s1.symmetric_difference_update(s2) Päivitä s1 sisältämään s1:n ja s2:n symmetrinen leikkaus
s1 ^= s2 Sama
s.add(elem) Lisää elem joukkoon s
s.remove(elem) Poista elem joukosta s. KeyError jos elem:iä ei löydy
s.discard(elem) Poista elem joukosta, jos se on siellä.
s.pop() Poista ja palauta joku joukon alkio. KeyError jos joukko on tyhjä
s.clear() Poista kaikki joukon alkiot

Kokeile itse!

Parhaiten opit, kun kokeilet itse näitä.

Sanakirja eli dict

Sanakirja on olio joka yhdistää avaimen johonkin arvoon. Tätä varten on tarjolla tyyppi dict, jonka oliot ovat muuttuvia.

Avain voi olla miltei mikä tahansa olio; ainoa rajoite on, että oliolla on oltava operaatio hash. Tätä ei ole määritetty muuttuville olioille, joiden yhtäsuuruutta verrataan olion identiteetin sijaan sisällön perusteella. Näitä ovat list, set ja dict -oliot. Lisäksi tuple ja frozenset -oliot, jotka sinänsä ovat muuttuvia, mutta joihin sisältyy list, set ja dict -olioita eivät kelpaa. Huomaa, että itse määritettyjen luokkien oliot kelpaavat, koska niitä verrataan aina olion identiteetin eikä sisällön perusteella.

Avaimelle talletettava arvo puolestaan voi olla aivan mikä tahansa.

Sanakirjan voi luoda kahdella tavalla, joko antamalla avain - arvo parit sulkujen {} välissä tai dict-konstruktorilla.

d1 = {}

d2 = { 'Antti': 10, 'Pentti': 20 }

d3 = { 10: 'Antti' }

d4 = { 'Antti': [1, 2, 3], ('x', 'y'): { (1, 2): 3 }}

# Five ways to create the same dict

a = dict(one=1, two=2, three=3)

b = {'one': 1, 'two': 2, 'three': 3}

c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))

d = dict([('two', 2), ('one', 1), ('three', 3)])

e = dict({'three': 3, 'one': 1, 'two': 2})

a == b == c == d == e

Seuraavat operaatiot ovat tarjolla:

Operaatio Kuvaus
len(d) Avain - arvo -parien määrä
d[key] Palauta avaimeen liittyvä arvo. Tuota KeyError jos avainta ei löydy d:stä.
d[key] = value Aseta arvo avaimelle
del d[key] Poista avain - arvo -pari tuota KeyError jos avainta ei löydy
key in d Onko avain määritetty
key not in d Edellisen negaatio
iter(d) Palauta iteraattori, joka tuottaa kaikki avaimet
d.clear() Poista kaikki avain - arvo -parit
d.copy() Palauta matala kopio
classmethod fromkeys(seq[, value]) Tee dict, jossa on seq:n sisältämät avaimet ja niiden jokaisen arvona on value (oletusarvo None)
d.get(key[, default]) Palauta avaimen arvo tai default, jos ei määritetty.
d.items() Palauta iterable (view), joka sisältää avain - arvo -parit
keys() Palauta iterable (view), joka sisältää avaimet.
d.pop(key[, default]) Poista avain ja palauta sitä vastaava arvo tai default. Jos avainta ei löydy tuota KeyError.
d.popitem() Poista mielivaltainen avain ja sitä vastaava arvo. Tuota KeyError, jos d tyhjä.
d.setdefault(key[, default]) Kuin get yllä, mutta myös asettaa arvoksi default, jos avainta ei löydy.
d.update([other]) Vaihda d:n avain - arvo -pareiksi otherin vastaavat.
values() Palauta iterable (view), joka sisältää arvot.

Merkkijono str

Merkkijono on keskeinen Pythonin tarjoama tyyppi. Merkkijonoja voi luoda monella tavalla: merkkijonoliteraaleilla, yhdistelemällä merkkijonoja toisiinsa sekä formatoimalla.

Merkkijonoliteraaleja voi tehdä seuraavasti:

'Simple string using single quotes'

"The same with double quotes"

"You can put ' characters in a string with double quotes"

'You can put " characters in a string with double quotes'

"You can escape chars with special meaning. Like \' or \\"

"""Multiple line literals can
be easily created using
three " characters as separators"""

'''
The same can be done with ' characters.
'''

r'A raw string literal takes even backslashes \ literally'

Merkkijonoja liimataan toisiinsa + operaattorilla:

s="""You can glue all kinds of string literals or
string variables together."""
t="Isn't this nice"

s+'\n\n'+ t

Formatoituja eli muotoiltuja merkkijonoja voi muodostaa joko metodilla str.format tai vanhemmalla printf-tyylisellä menetelmällä, joka on peräisin C-ohjelmointikielestä. Nämä molemmat sisältävät joukon merkintätapoja, joilla merkkijonoista saa siistin näköisiä.

Metodia str.format varten on määritetty oma kielensä (ks. The Python Standard Library: 6.1.3. Format String Syntax.

Merkkijono-operaatioita on tarjolla paljon, joten itse ei monesti tarvi ohjelmoida tarvitsemaansa toimintoa. Merkkijonojen dokumentaatioon kannattaa tutustua Python Standard Libraryn luvussa 4.7. Text Sequence Type.

Palaute

«  2. Kierros 2 (29.1.2016 kello 23:59)   ::   Etusivulle   ::   2.2. Pythonin poikkeukset  »