A Python listamegértés jelölés használata

Üzleti

A Pythonban egyszerűen használhatjuk a listaszerű jelölést egy új lista létrehozásakor.(List comprehensions)

Ebben a cikkben először a következőket fogjuk megvitatni

  • A lista megértésének alaptípusa
  • Listamegértés jelölés feltételes elágazással az if segítségével
  • Kombináció terner operátorokkal (if else-szerű feldolgozás)
  • zip(),enumerate()Ezekkel kombinálva
  • beágyazott lista felvétele jelölés

Ezután a listamegértés jelöléskészletét ismertetjük példakóddal.

  • halmazbefogadási jelölés(Set comprehensions)
  • szótárba való felvétel jelölése(Dict comprehensions)
  • generátor típusa(Generator expressions)

A lista megértésének alaptípusa

A listamegértés jelölése a következőképpen írható.

[Expression for Any Variable Name in Iterable Object]

Egy iterálható objektum, például egy lista, tuple vagy tartomány minden egyes elemét egy tetszőleges változó névvel veszi, és egy kifejezéssel értékeli ki. Egy új listát ad vissza, amelynek eleme az értékelési eredmény.

Egy példát adunk egy egyenértékű for utasítással együtt.

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]

Ugyanez a folyamat elvégezhető a map() paranccsal is, de az egyszerűség és az áttekinthetőség miatt a listamegértés jelölése előnyösebb.

Listamegértés jelölés feltételes elágazással az if segítségével

A feltételes elágazás if segítségével is lehetséges. Írjuk az if-et az utótagba a következőképpen.

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

Az iterábilis objektumnak csak azokat az elemeit értékeli ki a kifejezés, amelyek feltételes kifejezése igaz, és egy új listát ad vissza, amelynek elemei az eredmény.

A feltételes kifejezésben bármilyen változó neve használható.

Egy példát adunk egy egyenértékű for utasítással együtt.

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]

Ugyanez a folyamat elvégezhető a filter() paranccsal is, de az egyszerűség és áttekinthetőség miatt a listamegértés jelölése előnyösebb.

Kombináció terner operátorokkal (if else-szerű feldolgozás)

A fenti példában csak azokat az elemeket dolgozza fel a rendszer, amelyek megfelelnek a kritériumoknak, és azokat, amelyek nem felelnek meg a kritériumoknak, kizárja az új listából.

Ha a feltétel függvényében szeretnénk váltani a folyamatot, vagy ha a feltételnek nem megfelelő elemeket másképp akarjuk feldolgozni, mint az if else-ben, használjuk a terner operátort.

Pythonban a terner operátor a következőképpen írható le

Value When True if Conditional Expression else Value When False

Ezt a listamegértési jelölés kifejezésrészében használjuk az alábbiakban látható módon.

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

Egy példát adunk egy egyenértékű for utasítással együtt.

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

Az igaz és hamis értékek tetszőleges változóneveket használó kifejezések írására is lehetőség van.

Ha a feltétel teljesül, akkor valamilyen feldolgozás történik, ellenkező esetben az eredeti iterábilis objektum értéke változatlan marad.

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]

Kombináció a zip() és enumerate() függvényekkel

A for utasításban gyakran használt hasznos függvények közé tartozik a zip(), amely több iteráblát kombinál, és az enumerate(), amely egy értéket ad vissza az indexével együtt.

Természetesen lehetséges a zip() és enumerate() használata a listakifejtés jelölésével is. Ez nem egy speciális szintaxis, és nem is nehéz, ha figyelembe vesszük a for utasítással való megfeleltetést.

Példa a zip() használatára.

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

Példa a enumerate() használatára.

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

Az ötlet ugyanaz, mint korábban az if használatakor.

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

Minden elem egy új elem kiszámításához is felhasználható.

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]

beágyazott lista felvétele jelölés

A for ciklusok egymásba ágyazásához hasonlóan a listamegértési jelölés is egymásba ágyazható.

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

A könnyebbség kedvéért sortörések és behúzások kerültek beillesztésre, de a nyelvtan szempontjából nem szükségesek; egy sorban is folytathatók.

Egy példát adunk egy egyenértékű for utasítással együtt.

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]

Lehetőség van több változó használatára is.

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

Lehetőség van feltételes elágazásra is.

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

Lehetőség van az egyes iterálható objektumok feltételes elágazására is.

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

halmazbefogadási jelölés(Set comprehensions)

Ha a szögletes zárójeleket [] a lista megértésének jelölésében görbe zárójelre {} cseréljük, akkor egy halmazt (halmaz típusú objektumot) hozunk létre.

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

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

szótárba való felvétel jelölése(Dict comprehensions)

A szótárak (dict típusú objektumok) szintén létrehozhatók a megértés jelöléssel.

{}, és adja meg a kulcsot és az értéket a kifejezésrészben kulcs: érték formában.

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

A kulcshoz és az értékhez bármilyen kifejezés megadható.

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

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

A kulcsok és értékek listájából új szótár létrehozásához használja a zip() függvényt.

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}

generátor típusa(Generator expressions)

Ha a listamegértés jelölésében a [] szögletes zárójeleket kerek zárójelek () helyett használjuk, akkor egy generátort kapunk vissza egy tuple helyett. Ezt generátoros kifejezéseknek nevezzük.

Példa a lista megértésének jelölésére.

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

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

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

Példa egy generátor kifejezésre. Ha a generátort úgy nyomtatod ki, ahogy van, akkor nem fogja kiírni a tartalmát, de ha egy for utasítással futtatod, akkor megkaphatod a tartalmát.

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

A generátor kifejezések lehetővé teszik a feltételes elágazást és egymásba ágyazást is az if, valamint a lista megértése jelölés használatával.

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)

Például, ha egy nagyszámú elemet tartalmazó listát a listamegértés jelöléssel generálunk, majd egy for utasítással végigmegyünk rajta, akkor a listamegértés jelölés használata esetén az összes elemet tartalmazó lista már az elején létrejön. Másrészt, ha generátor-kifejezést használunk, akkor a ciklus minden egyes ismétlésénél az elemek egyenként generálódnak, így csökkentve a felhasznált memória mennyiségét.

Ha a generátor kifejezés a függvény egyetlen argumentuma, a kerek zárójelek () elhagyhatók.

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

Ami a feldolgozási sebességet illeti, a listamegértés jelölés gyakran gyorsabb, mint a generátor jelölés, ha minden elemet feldolgozunk.

Azonban például az all() vagy az any() értékelésekor az eredményt akkor határozzuk meg, ha false vagy true van jelen, így a generátoros kifejezések használata gyorsabb lehet, mint a listamegértési jelölés használata.

Nincs tuple megértési jelölés, de ha a tuple() argumentumaként egy generátor kifejezést használunk, akkor létrehozhatunk egy tuple-t a megértési jelölésben.

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

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

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