List comprehension

Jedním z nejběžnějších datových typů v Pythonu je list (seznam). Seznamy během svého života často prochází určitým procesem - někde se vytvoří, v další funkci dojde k jejich úpravě, při které se některé položky odeberou, jinde se vytvoří seznam nový, který obsahuje položky původního seznamu, které ale prošly nějakou transformací a na konci se celý seznam přečte. Pojďme se podívat na tři základní způsoby, pomocí kterých můžeme vyrobit nový seznam.

Generování seznamu pomocí cyklu

Jedná se o nejpřímočařejší způsob, jakým vyrobit nový seznam. Před cyklem samotným si vytvoříme prázdný list a v každém průchodu smyčkou pomocí funkce append() přidáme požadovanou položku. Tou může být číslo, nebo třeba instance nějaké třídy.

import random

class RandomObject:
    def __init__(self, x, y, object_type):
        self.x = x
        self.y = y
        self.object_type = object_type

    def __repr__(self):
        return f"({self.x},{self.y},{self.object_type})"

data = []

for i in range(10):
    x = random.randint(0,10)
    y = random.randint(0,10)
    object_type = random.choice(["tree", "rock", "car"])

    data.append(RandomObject(x, y, object_type))

print(data)

Uvnitř for bloku můžeme provést libovolnou kontrolu, na základě které nakonec položku do seznamu přidáme nebo ne. Například nemusíme chtít přidat dvě položky, které mají shodné x-ové a y-ové souřadnice. V tom případě pomocí ifu ošetříme řádek s data.append(), aby se položka do seznamu nepřidala.

Map a Filter

Milovníci funkcionálního programování jistě ocení průchod a generování seznamů pomocí funkcí map() a filter(). Pomocí funkce map() dokážeme jednoduše modifikovat každou položku seznamu na jinou a pomocí filter() ji do nového seznamu buď přidáme nebo ne. Jakým způsobem se má položka transformovat nebo zda se má do seznamu vůbec přidat, definujeme pomocí funkce, kterou do map() a filter() pošleme taky. Často se na tomto místě setkáme s tzv. lambda funkcí - anonymní funkcí, která nemá jméno a není definována pomocí klíčového slova def. Momentálně nám stačí pouze doporučení, že lambda funkce dobře poslouží u krátkých operací. U komplexnějších raději sáhněte po klasickém zápisu s klíčovým slovem def.

def extract_object_type(item):
    return item.object_type

object_type_only = list(map(extract_object_type, data))
print(object_type_only)

coordinates_only = list(map(lambda item: (item.x, item.y), data))
print(coordinates_only)

Do první proměnné object_type_only jsme si uložili seznam, který vznikl tak, že jsme každou položku původního seznamu (data) prohnali funkcí extract_object_type. Druhá proměnná coordinates_only vznikla tak, že jsme pomocí lambda funkce vytáhli souřadnice každého objektu ze seznamu. Všimněte si, že výsledek volání map() je ještě obalen funkcí list(), jelikož map() vrací iterátor.

Co kdybychom chtěli vyrobit nový seznam, který bude obsahovat jenom položky typu "rock"? V tom případě sáhneme po funkci filter().

rocks_only = list(filter(lambda item: item.object_type == "rock", data))
print(rocks_only)

Prvním argumentem pro filter() je funkce, která zde hraje roli podmínky. Pokud je podmínka splněna (funkce vrátí True), aktuální položka se přidá do nového listu. Pokud podmínka splněna není (funkce vrátí False), položka se do nového listu nepřidá.

List comprehension

List comprehension je další způsob, jak vyrobit nový seznam a rovnou jej naplnit. Podobně jako funkce map() a filter() nabízí možnost položky rovnou při průchodu modifikovat, případně filtrovat pouze ty, které do nového seznamu chceme opravdu přidat. List comprehension je velice populární díky svému elegantnímu zápisu, který však může být dvousečnou zbraní. Někteří vývojáři používají list comprehension i pro komplexnější úlohy na úkor čitelnosti kódu - tohle, prosím, nedělejte 🙂.

Zápis list comprehension

List comprehension - zápis

Příklad č.1 - vygenerování seznamu náhodných čísel

import random

random_numbers_list = [random.random() for _ in range(10)]

Příklad č.2 - seznam druhých mocnin čísel 1-10

power_list = [i*i for i in range(10)]

Příklad č.3 - vytvoření seznamu položek, které splňují nějakou podmínku

Zde si vypůjčíme vytvořenou třídu výše a dříve vytvořený seznam data. Tento zápis pomocí list comprehension je shodný s tím v odstavci o funkci filter().

rocks_only = [item for item in data if item.object_type == "rock"]

Příklad č.4 - vytvoření matice, dvourozměrného pole

matrix = [[x*y for x in range(1,11)] for y in range(1,11)]

Tímto zápisem jsme vytvořili matici s "malou násobilkou". Jak vidíte, výraz, který chceme vložit do nového listu může být opět zápisem pomocí list comprehension. Zde se už nabízí prostor pro komplexitu a horší čitelnost. Ve chvíli, kdy se k tomu přidá ještě podmínková část, může tento zápis být pro vývojáře, který bude kód číst po vás, trošku nepřehledný.

Úroveň znalostí
Začátečník