Použití notace seznamů v jazyce Python

Podnikání

V jazyce Python lze při generování nového seznamu jednoduše použít notaci pro porozumění seznamu.(List comprehensions)

V tomto článku se budeme nejprve zabývat následujícími otázkami.

  • Základní typ zápisu pro porozumění seznamu
  • Zápis pro porozumění seznamu s podmíněným větvením pomocí if
  • Kombinace s ternárními operátory (zpracování podobné if else)
  • zip(),enumerate()Kombinace s těmito
  • zápis vnořeného seznamu

Dále vysvětlíme sadu notací pro porozumění seznamům s ukázkovým kódem.

  • notace začlenění sady(Set comprehensions)
  • slovníková inkluzivní notace(Dict comprehensions)
  • typ generátoru(Generator expressions)

Základní typ zápisu pro porozumění seznamu

Zápis pro porozumění seznamu se zapisuje takto.

[Expression for Any Variable Name in Iterable Object]

Vezme každý prvek iterovatelného objektu, jako je seznam, tuple nebo rozsah, podle libovolného názvu proměnné a vyhodnotí jej výrazem. Vrátí se nový seznam s výsledkem vyhodnocení jako prvkem.

Příklad je uveden spolu s ekvivalentním příkazem for.

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]

Stejný postup lze provést i pomocí funkce map(), ale pro její jednoduchost a přehlednost se upřednostňuje zápis s porozuměním seznamu.

Zápis pro porozumění seznamu s podmíněným větvením pomocí if

Možné je také podmíněné větvení pomocí if. Zapište if v postfixu takto.

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

Výrazem se vyhodnotí pouze ty prvky iterovatelného objektu, jejichž podmíněný výraz je pravdivý, a vrátí se nový seznam, jehož prvky jsou výsledkem.

V podmíněném výrazu můžete použít libovolný název proměnné.

Příklad je uveden spolu s ekvivalentním příkazem for.

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]

Stejný postup lze provést i pomocí funkce filter(), ale pro její jednoduchost a přehlednost se upřednostňuje zápis s porozuměním seznamu.

Kombinace s ternárními operátory (zpracování podobné if else)

Ve výše uvedeném příkladu jsou zpracovány pouze prvky, které splňují kritéria, a ty, které kritéria nesplňují, jsou z nového seznamu vyloučeny.

Chcete-li přepínat proces v závislosti na podmínce nebo chcete-li prvky, které podmínku nesplňují, zpracovat jinak, jako v případě if else, použijte ternární operátor.

V jazyce Python lze ternární operátor zapsat takto

Value When True if Conditional Expression else Value When False

Používá se ve výrazové části notace pro porozumění seznamu, jak je uvedeno níže.

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

Příklad je uveden spolu s ekvivalentním příkazem for.

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

Je také možné psát výrazy s použitím libovolných názvů proměnných pro hodnoty true a false.

Pokud je podmínka splněna, provede se nějaké zpracování, jinak se hodnota původního iterovatelného objektu ponechá beze změny.

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]

Kombinace s funkcemi zip() a enumerate()

Mezi užitečné funkce, které se často používají v příkazu for, patří zip(), která kombinuje více iterací, a enumerate(), která vrací hodnotu spolu s jejím indexem.

Samozřejmě je možné použít zip() a enumerate() s notací pro porozumění seznamu. Nejedná se o žádnou zvláštní syntaxi a není to nijak složité, pokud uvážíte korespondenci s příkazem for.

Příklad funkce zip().

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říklad funkce enumerate().

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

Myšlenka je stejná jako dříve při použití if.

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

Každý prvek lze také použít k výpočtu nového prvku.

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]

zápis vnořeného seznamu

Stejně jako vnořené smyčky for lze vnořovat i zápisy pro porozumění seznamu.

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

Pro větší pohodlí byly přidány zalomení řádků a odsazení, které však nejsou nutné pro gramatiku; mohou pokračovat na jednom řádku.

Příklad je uveden spolu s ekvivalentním příkazem for.

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]

Je také možné použít více proměnných.

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

Můžete také provádět podmíněné větvení.

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

Je také možné podmíněně větvit pro každý iterovatelný objekt.

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

notace začlenění sady(Set comprehensions)

Změnou hranatých závorek [] v notaci pro pochopení seznamu na kulaté závorky {} se vytvoří množina (objekt typu množina).

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

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

slovníková inkluzivní notace(Dict comprehensions)

Slovníky (objekty typu dict) lze generovat také pomocí notace porozumění.

{} a ve výrazové části zadejte klíč a hodnotu jako key: value.

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

Pro klíč a hodnotu lze zadat libovolný výraz.

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

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

Chcete-li vytvořit nový slovník ze seznamu klíčů a hodnot, použijte funkci 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}

typ generátoru(Generator expressions)

Pokud jsou hranaté závorky [] v notaci pro porozumění seznamu použity jako kulaté závorky (), je místo tuple vrácen generátor. Tomu se říká generátor výrazů.

Příklad zápisu pro porozumění seznamu.

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

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

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

Příklad generátorového výrazu. Pokud generátor vytisknete() tak, jak je, nevypíše svůj obsah, ale pokud jej spustíte s příkazem for, můžete jeho obsah získat.

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

Generátorové výrazy umožňují také podmíněné větvení a vnořování pomocí if a notace pro porozumění seznamu.

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)

Například pokud je seznam s velkým počtem prvků generován pomocí zápisu s porozuměním seznamu a poté procházíme smyčkou pomocí příkazu for, bude při použití zápisu s porozuměním seznamu seznam obsahující všechny prvky vygenerován na začátku. Na druhou stranu, pokud použijete generátorový výraz, při každém opakování smyčky se prvky generují po jednom, čímž se sníží množství použité paměti.

Pokud je generátorový výraz jediným argumentem funkce, lze kulaté závorky () vynechat.

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

Co se týče rychlosti zpracování, zápis s porozuměním seznamu je často rychlejší než zápis s generátorem, pokud jsou zpracovány všechny prvky.

Při posuzování například pomocí all() nebo any() se však výsledek určuje podle toho, zda je přítomna hodnota false nebo true, takže použití generátorových výrazů může být rychlejší než použití zápisu pro porozumění seznamu.

Neexistuje notace pro porozumění tuplům, ale pokud jako argument funkce tuple() použijete výraz generátoru, můžete generovat tuple v notaci pro porozumění.

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

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

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