Pythonissa on yksinkertaista käyttää listan ymmärrettävyyden merkintätapaa uutta listaa luotaessa.(List comprehensions
)
- 5. Data Structures — List Comprehensions — Python 3.10.0 Documentation
- 6. Expressions — Displays for lists, sets and dictionaries — Python 3.10.0 Documentation
Tässä artikkelissa keskustelemme ensin seuraavista asioista
- Luettelon ymmärtämisen perustyyppi
- Luettelon ymmärtämisen merkintätapa ehdollisella haarautumisella if:n avulla
- Yhdistelmä ternaaristen operaattoreiden kanssa (if else-tyyppinen käsittely)
zip()
,enumerate()
Yhdistelmä näiden kanssa- sisäkkäisen listan sisällyttämisen merkintätapa
Seuraavaksi selitämme listan ymmärtämisen merkintätavan esimerkkikoodin avulla.
- set inclusion notation(
Set comprehensions
) - sanakirjan sisällyttämisen merkintätapa(
Dict comprehensions
) - generaattorin tyyppi(
Generator expressions
)
- Luettelon ymmärtämisen perustyyppi
- Luettelon ymmärtämisen merkintätapa ehdollisella haarautumisella if:n avulla
- Yhdistelmä ternaaristen operaattoreiden kanssa (if else-tyyppinen käsittely)
- Yhdistelmä zip() ja enumerate() kanssa
- sisäkkäisen listan sisällyttämisen merkintätapa
- set inclusion notation(Set comprehensions)
- sanakirjan sisällyttämisen merkintätapa(Dict comprehensions)
- generaattorin tyyppi(Generator expressions)
Luettelon ymmärtämisen perustyyppi
Luettelon ymmärtämisen merkintätapa kirjoitetaan seuraavasti.
[Expression for Any Variable Name in Iterable Object]
Se ottaa iteroitavan objektin, kuten listan, tuplen tai alueen, jokaisen elementin mielivaltaisella muuttujanimellä ja arvioi sen lausekkeella. Palautetaan uusi lista, jonka elementtinä on arvioinnin tulos.
Esimerkki annetaan yhdessä vastaavan for-lausekkeen kanssa.
squares = [i**2 for i in range(5)]
print(squares)
# [0, 1, 4, 9, 16]
squares = []
for i in range(5):
squares.append(i**2)
print(squares)
# [0, 1, 4, 9, 16]
Sama prosessi voidaan tehdä myös funktiolla map(), mutta yksinkertaisuutensa ja selkeytensä vuoksi suositaan listan ymmärtämisen merkintätapaa.
Luettelon ymmärtämisen merkintätapa ehdollisella haarautumisella if:n avulla
Ehdollinen haarautuminen if-olion avulla on myös mahdollista. Kirjoita if postfixiin seuraavasti.
[Expression for Any Variable Name in Iterable Object if Conditional Expression]
Vain ne iterable-olion elementit, joiden ehdollinen lauseke on tosi, arvioidaan lausekkeella, ja palautetaan uusi lista, jonka elementit ovat tulos.
Voit käyttää ehdollisessa lausekkeessa mitä tahansa muuttujan nimeä.
Esimerkki annetaan yhdessä vastaavan for-lausekkeen kanssa.
odds = [i for i in range(10) if i % 2 == 1]
print(odds)
# [1, 3, 5, 7, 9]
odds = []
for i in range(10):
if i % 2 == 1:
odds.append(i)
print(odds)
# [1, 3, 5, 7, 9]
Sama prosessi voidaan tehdä myös funktiolla filter(), mutta yksinkertaisuutensa ja selkeytensä vuoksi suositaan listan ymmärtämisen merkintätapaa.
Yhdistelmä ternaaristen operaattoreiden kanssa (if else-tyyppinen käsittely)
Yllä olevassa esimerkissä käsitellään vain ne elementit, jotka täyttävät kriteerit, ja ne elementit, jotka eivät täytä kriteerejä, jätetään pois uudesta luettelosta.
Jos haluat vaihtaa prosessia ehdon mukaan tai jos haluat käsitellä eri tavalla elementtejä, jotka eivät täytä ehtoa, kuten if else -operaattorissa, käytä ternääristä operaattoria.
Pythonissa ternäärinen operaattori voidaan kirjoittaa seuraavasti
Value When True if Conditional Expression else Value When False
Tätä käytetään luettelon ymmärtämisen merkintätavan lausekeosassa alla esitetyllä tavalla.
[Value When True if Conditional Expression else Value When False for Any Variable Name in Iterable Object]
Esimerkki annetaan yhdessä vastaavan for-lausekkeen kanssa.
odd_even = ['odd' if i % 2 == 1 else 'even' for i in range(10)]
print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
odd_even = []
for i in range(10):
if i % 2 == 1:
odd_even.append('odd')
else:
odd_even.append('even')
print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
On myös mahdollista kirjoittaa lausekkeita, joissa käytetään mielivaltaisia muuttujien nimiä tosia ja vääriä arvoja varten.
Jos ehto täyttyy, suoritetaan jokin käsittely, muuten alkuperäisen iterable-olion arvo jätetään ennalleen.
odd10 = [i * 10 if i % 2 == 1 else i for i in range(10)]
print(odd10)
# [0, 10, 2, 30, 4, 50, 6, 70, 8, 90]
Yhdistelmä zip() ja enumerate() kanssa
Hyödyllisiä funktioita, joita käytetään usein for-lausekkeessa, ovat zip(), joka yhdistää useita iteraattoreita, ja enumerate(), joka palauttaa arvon ja sen indeksin.
Tietenkin on mahdollista käyttää zip()- ja enumerate()-funktioita listan ymmärtämisen merkintätavalla. Se ei ole erityinen syntaksi, eikä se ole vaikeaa, jos otetaan huomioon vastaavuus for-lauseen kanssa.
Esimerkki zip() -toiminnosta.
l_str1 = ['a', 'b', 'c']
l_str2 = ['x', 'y', 'z']
l_zip = [(s1, s2) for s1, s2 in zip(l_str1, l_str2)]
print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]
l_zip = []
for s1, s2 in zip(l_str1, l_str2):
l_zip.append((s1, s2))
print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]
Esimerkki enumerate() -toiminnosta.
l_enu = [(i, s) for i, s in enumerate(l_str1)]
print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]
l_enu = []
for i, s in enumerate(l_str1):
l_enu.append((i, s))
print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]
Ajatus on sama kuin aiemmin käytettäessä if:tä.
l_zip_if = [(s1, s2) for s1, s2 in zip(l_str1, l_str2) if s1 != 'b']
print(l_zip_if)
# [('a', 'x'), ('c', 'z')]
Jokaista elementtiä voidaan käyttää myös uuden elementin laskemiseen.
l_int1 = [1, 2, 3]
l_int2 = [10, 20, 30]
l_sub = [i2 - i1 for i1, i2 in zip(l_int1, l_int2)]
print(l_sub)
# [9, 18, 27]
sisäkkäisen listan sisällyttämisen merkintätapa
Kuten for-silmukoita, myös listan ymmärtämisen merkintätapa voi olla sisäkkäinen.
[Expression for Variable Name 1 in Iterable Object 1
for Variable Name 2 in Iterable Object 2
for Variable Name 3 in Iterable Object 3 ... ]
Mukavuuden vuoksi on lisätty rivinvaihdot ja sisennykset, mutta niitä ei tarvita kieliopin kannalta; niitä voidaan jatkaa yhdellä rivillä.
Esimerkki annetaan yhdessä vastaavan for-lausekkeen kanssa.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
flat = []
for row in matrix:
for x in row:
flat.append(x)
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
On myös mahdollista käyttää useita muuttujia.
cells = [(row, col) for row in range(3) for col in range(2)]
print(cells)
# [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
Voit myös tehdä ehdollisen haaroituksen.
cells = [(row, col) for row in range(3)
for col in range(2) if col == row]
print(cells)
# [(0, 0), (1, 1)]
On myös mahdollista haarautua ehdollisesti jokaisen iteroitavan objektin kohdalla.
cells = [(row, col) for row in range(3) if row % 2 == 0
for col in range(2) if col % 2 == 0]
print(cells)
# [(0, 0), (2, 0)]
set inclusion notation(Set comprehensions)
Vaihtamalla listan ymmärtämisen merkintätavan hakasulkeet [] sulkeisiin {} luodaan joukko (joukko-tyyppinen objekti).
{Expression for Any Variable Name in Iterable Object}
s = {i**2 for i in range(5)}
print(s)
# {0, 1, 4, 9, 16}
sanakirjan sisällyttämisen merkintätapa(Dict comprehensions)
Sanakirjoja (dict-tyyppisiä objekteja) voidaan luoda myös comprehension-notaatiolla.
{} ja määritä avain ja arvo lausekeosassa muodossa avain: arvo.
{Key: Value for Any Variable Name in Iterable Object}
Avaimelle ja arvolle voidaan määrittää mikä tahansa lauseke.
l = ['Alice', 'Bob', 'Charlie']
d = {s: len(s) for s in l}
print(d)
# {'Alice': 5, 'Bob': 3, 'Charlie': 7}
Voit luoda uuden sanakirjan avainten ja arvojen luettelosta käyttämällä funktiota zip().
keys = ['k1', 'k2', 'k3']
values = [1, 2, 3]
d = {k: v for k, v in zip(keys, values)}
print(d)
# {'k1': 1, 'k2': 2, 'k3': 3}
generaattorin tyyppi(Generator expressions)
Jos listan ymmärtämisen merkinnässä käytetään hakasulkeita [] pyöreiden hakasulkeiden () sijasta, palautetaan generaattori tuplen sijasta. Tätä kutsutaan generaattorilausekkeiksi.
Esimerkki luettelon ymmärtämisen merkintätavoista.
l = [i**2 for i in range(5)]
print(l)
# [0, 1, 4, 9, 16]
print(type(l))
# <class 'list'>
Esimerkki generaattorin lausekkeesta. Jos tulostat() generaattorin sellaisenaan, se ei tulosta sisältöään, mutta jos suoritat sen for-lausekkeella, saat sisällön näkyviin.
g = (i**2 for i in range(5))
print(g)
# <generator object <genexpr> at 0x10af944f8>
print(type(g))
# <class 'generator'>
for i in g:
print(i)
# 0
# 1
# 4
# 9
# 16
Generaattorilausekkeet mahdollistavat myös ehdollisen haarautumisen ja sisäkkäisyyden käyttämällä if-merkintätapaa sekä listan ymmärtämisen merkintätapaa.
g_cells = ((row, col) for row in range(0, 3)
for col in range(0, 2) if col == row)
print(type(g_cells))
# <class 'generator'>
for i in g_cells:
print(i)
# (0, 0)
# (1, 1)
Jos esimerkiksi luettelo, jossa on suuri määrä elementtejä, luodaan käyttämällä luettelon ymmärtämisen merkintätapaa ja sen jälkeen luodaan silmukka for-lauseella, kaikki elementit sisältävä luettelo luodaan alussa, jos käytetään luettelon ymmärtämisen merkintätapaa. Jos taas käytetään generaattorilauseketta, elementit luodaan yksi kerrallaan joka kerta, kun silmukka toistetaan, mikä vähentää käytettävän muistin määrää.
Jos generaattorilauseke on funktion ainoa argumentti, pyöreät sulut () voidaan jättää pois.
print(sum([i**2 for i in range(5)]))
# 30
print(sum((i**2 for i in range(5))))
# 30
print(sum(i**2 for i in range(5)))
# 30
Käsittelynopeuden osalta listan ymmärtämisen merkintätapa on usein nopeampi kuin generaattorin merkintätapa, kun kaikki elementit käsitellään.
Kuitenkin esimerkiksi all()- tai any()-toiminnoilla arvioitaessa tulos määräytyy, kun false tai true on läsnä, joten generaattorilausekkeiden käyttäminen voi olla nopeampaa kuin listan ymmärtämisen merkintätavan käyttäminen.
Tuplan ymmärtämisen merkintätapaa ei ole olemassa, mutta jos käytät generaattorilauseketta tuple()-olion argumenttina, voit luoda tuplan ymmärtämisen merkintätavan mukaisesti.
t = tuple(i**2 for i in range(5))
print(t)
# (0, 1, 4, 9, 16)
print(type(t))
# <class 'tuple'>