Pythoni loendi arusaadavuse notatsiooni kasutamine

Äri

Pythonis on uue loendi genereerimisel lihtne kasutada loendi arusaadavuse notatsiooni.(List comprehensions)

Selles artiklis arutame kõigepealt järgmist.

  • Loetelu mõistmise põhitüüp
  • Loetelu mõistmise notatsioon tingimusliku hargnemisega if abil
  • Kombinatsioon ternaarse operaatoriga (if else-taoline töötlemine)
  • zip(),enumerate()Kombinatsioon nendega
  • nested list inclusion notation

Järgnevalt selgitame nimekirja mõistmise notatsioonikomplekti koos näidiskoodiga.

  • komplekti kaasamise märkimine(Set comprehensions)
  • sõnastiku kaasamise märkimine(Dict comprehensions)
  • generaatori tüüp(Generator expressions)

Loetelu mõistmise põhitüüp

Loetelu mõistmine kirjutatakse järgmiselt.

[Expression for Any Variable Name in Iterable Object]

See võtab iga iteratiivse objekti, näiteks loendi, tupli või vahemiku elemendi suvalise muutuja nime järgi ja hindab seda väljendi abil. Tagastatakse uus nimekiri, mille elemendiks on hindamistulemus.

Näide on esitatud koos samaväärse for-avaldusega.

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 protsessi saab teha ka funktsiooniga map(), kuid selle lihtsuse ja selguse tõttu eelistatakse nimekirja mõistmise notatsiooni.

Loetelu mõistmise notatsioon tingimusliku hargnemisega if abil

Võimalik on ka tingimuslik hargnemine if-iga. Kirjutage if postfixi järgmiselt.

[Expression for Any Variable Name in Iterable Object if Conditional Expression]

Väljendiga hinnatakse ainult neid iteratiivse objekti elemente, mille tingimusavaldis on tõene, ja tagastatakse uus loend, mille elemendid on tulemus.

Tingimusavalduses võib kasutada mis tahes muutuja nime.

Näide on esitatud koos samaväärse for-avaldusega.

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 protsessi saab teha ka filter() abil, kuid selle lihtsuse ja selguse tõttu eelistatakse nimekirja mõistmise notatsiooni.

Kombinatsioon ternaarse operaatoriga (if else-taoline töötlemine)

Ülaltoodud näites töödeldakse ainult neid elemente, mis vastavad kriteeriumidele, ja need, mis ei vasta kriteeriumidele, jäetakse uuest nimekirjast välja.

Kui soovite protsessi sõltuvalt tingimusest ümber lülitada või kui soovite tingimusele mittevastavaid elemente erinevalt töödelda, nagu näiteks if else, kasutage kolmikoperaatorit.

Pythonis saab ternaarse operaatori kirjutada järgmiselt

Value When True if Conditional Expression else Value When False

Seda kasutatakse loetelu mõistmise notatsiooni väljendusosas, nagu on näidatud allpool.

[Value When True if Conditional Expression else Value When False for Any Variable Name in Iterable Object]

Näide on esitatud koos samaväärse for-avaldusega.

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']

Samuti on võimalik kirjutada väljendeid, kasutades tõese ja vale väärtuse jaoks suvalisi muutujate nimesid.

Kui tingimus on täidetud, tehakse mingi töötlus, vastasel juhul jäetakse algse iteratiivse objekti väärtus muutmata.

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]

Kombinatsioon zip() ja enumerate() funktsioonidega

Kasulikud funktsioonid, mida sageli kasutatakse for-avalduses, on näiteks zip(), mis ühendab mitu iteratiivi, ja enumerate(), mis tagastab väärtuse koos selle indeksiga.

Loomulikult on võimalik kasutada zip() ja enumerate() funktsiooni koos loendi mõistmise notatsiooniga. See ei ole eriline süntaks ja see ei ole keeruline, kui arvestada vastavust for-avaldusega.

Näide zip() kohta.

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')]

Näide enumerate() funktsioonist.

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')]

Idee on sama, mis varem if-i kasutamisel.

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')]

Iga elementi saab kasutada ka uue elemendi arvutamiseks.

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]

nested list inclusion notation

Nagu pesitsevaid for-silmuseid, saab ka loendi mõistmise notatsiooni pesitseda.

[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 ... ]

Mugavuse huvides on lisatud reavahetused ja mõttekohad, kuid need ei ole grammatika seisukohalt vajalikud; neid võib jätkata ühel real.

Näide on esitatud koos samaväärse for-avaldusega.

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]

Samuti on võimalik kasutada mitut muutujat.

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)]

Saate teha ka tingimuslikke hargnemisi.

cells = [(row, col) for row in range(3)
         for col in range(2) if col == row]
print(cells)
# [(0, 0), (1, 1)]

Samuti on võimalik iga iteratiivse objekti jaoks tingimuslikult hargneda.

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)]

komplekti kaasamise märkimine(Set comprehensions)

Kui loetelust arusaamise märkimises muuta nurksulgud [] kumerate nurksulgude {} vastu, siis luuakse hulk (hulk-tüüpi objekt).

{Expression for Any Variable Name in Iterable Object}
s = {i**2 for i in range(5)}

print(s)
# {0, 1, 4, 9, 16}

sõnastiku kaasamise märkimine(Dict comprehensions)

Sõnastikke (dict-tüüpi objektid) saab genereerida ka arusaadavuse notatsiooniga.

{} ja täpsustage võti ja väärtus väljendi osas kui võti: väärtus.

{Key: Value for Any Variable Name in Iterable Object}

Võtme ja väärtuse jaoks võib määrata mis tahes väljendi.

l = ['Alice', 'Bob', 'Charlie']

d = {s: len(s) for s in l}
print(d)
# {'Alice': 5, 'Bob': 3, 'Charlie': 7}

Uue sõnastiku loomiseks võtmete ja väärtuste loendist kasutage funktsiooni 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}

generaatori tüüp(Generator expressions)

Kui loetelumärgenduses kasutatakse ümmarguste sulgude () asemel nurksulgusid [], tagastatakse tupli asemel generaator. Seda nimetatakse generaatorväljenditeks.

Näide loetelu mõistmise märkimisest.

l = [i**2 for i in range(5)]

print(l)
# [0, 1, 4, 9, 16]

print(type(l))
# <class 'list'>

Näide generaatori väljendist. Kui te print() generaatori sellisena, nagu ta on, ei trüki ta selle sisu välja, kuid kui te käivitate selle koos for-avaldusega, saate selle sisu kätte.

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

Generaatori väljendid võimaldavad ka tingimuslikku hargnemist ja pesitsemist, kasutades nii if- kui ka loendi mõistmise notatsiooni.

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)

Näiteks kui suure hulga elementidega loend genereeritakse loendi mõistmise märkega ja seejärel läbitakse loend for avaldusega, genereeritakse kõiki elemente sisaldav loend alguses, kui kasutatakse loendi mõistmise märket. Teisest küljest, kui kasutate generaatorväljendit, siis iga kord, kui silmust korratakse, genereeritakse elemendid ükshaaval, vähendades seega kasutatava mälu hulka.

Kui generaatorväljend on funktsiooni ainus argument, võib ümarsulgud () ära jätta.

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

Mis puutub töötlemiskiirusse, siis loetelust arusaamise märkimisviis on sageli kiirem kui generaatori märkimisviis, kui kõiki elemente töödeldakse.

Kuid näiteks all() või any() abil otsustades määratakse tulemus kindlaks, kui esineb false või true, seega võib generaatorväljendite kasutamine olla kiirem kui loendi mõistmise notatsiooni kasutamine.

Tupli mõistmise notatsioon puudub, kuid kui kasutate tuple() argumendina generaatorväljendit, saate genereerida tupli mõistmise notatsioonis.

t = tuple(i**2 for i in range(5))

print(t)
# (0, 1, 4, 9, 16)

print(type(t))
# <class 'tuple'>