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]
i
sidotaan vuorotellen syötteen alkioihinif i % 2 == 0
, joka valitsee syötteestä halutut alkiot (tämän voi jättää pois)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.
range(10)
on syötteenä käytettävä lista tai iterable