Zarábaj až 6 000 € mesačne! Akreditované rekvalifikačné kurzy od 0 €. Viac informácií.

7. diel - Komprehencie, lambda výrazy a funkcie v Pythone

V predchádzajúcom cvičení, Riešené úlohy k 4.-6. lekcii kolekcií v Pythone, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.

V tomto tutoriále kolekcií v Pythone si predstavíme niektoré pokročilejšie techniky funkcionálneho programovania v Pythone. Pozrieme sa na zoznamové, množinové a slovníkové komprehencie, lambda výrazy a na vstavané funkcie zip(), map() a filter().

Zoznamové komprehencie

Zoznamová komprehencie umožňuje stručný zápis tvorby zoznamu z iterovateľného objektu pomocou transformácie, iterácie a prípadne filtra. Príklad syntaxe je nasledujúci: new_list = [element * 2 for element in another_list if element < 5].

Jednotlivé fázy teda sú:

  • transformácia - výraz, ktorý definuje zmenu pôvodného prvku (tu element * 2),
  • iterácia - transformácia sa postupne vykonáva na jednotlivé prvky iterovateľného objektu (tu for element in another_list),
  • filter (nepovinný) - ak prvok z pôvodného iterovateľného objektu spĺňa danú podmienku, pridá sa do nového zoznamu, v opačnom prípade sa ignoruje (tu if element < 5).

Poďme si všetko ukázať v praxi. Na porovnanie použijeme aj klasický cyklus for, pomocou ktorého by sme docielili rovnaký výsledok:

source_list = [1, 2, 3, 4, 5]

# for loop:
new_list_1 = []
for element in source_list:
    if element < 5:
        new_list_1.append(element * 2)

# list comprehension:
new_list_2 = [element * 2 for element in source_list if element < 5]

print(new_list_1)
print(new_list_2)

Vo výstupe vidíme, že oba prístupy dávajú rovnaký výsledok:

Konzolová aplikácia
[2, 4, 6, 8]
[2, 4, 6, 8]

Vytvoriť zoznam týmto spôsobom sa odporúča iba v jednoduchých prípadoch. Čím zložitejšiu zoznamovú komprehenciu vytvoríme, tým menej čitateľná bude a môže to byť kontraproduktívna:

matrix = [
    [0, 1, 0],
    [1, 2, 1],
    [2, 0, 1],
 ]

# list comprehension:
ones = [number for row in matrix for number in row if number > 0 and number < 2]  # less readable

print(ones)

# for loop:
ones = []
for row in matrix:
    for number in row:
        if number > 0 and number < 2:
            ones.append(number)

print(ones)

Vo výstupe opäť vidíme, že oba prístupy dávajú rovnaký výsledok:

Konzolová aplikácia
[1, 1, 1, 1]
[1, 1, 1, 1]

Množinové komprehencie

Vyššie uvedený spôsob je možné aplikovať aj na množiny. Postup je takmer identický, jediný rozdiel je v použitých zátvorkách. Pri množinách sa namiesto hranatých používajú zložené:

Klikni pre editáciu
  • source_sequence = (1, 2, 3, 4, 5)
    
    double_generator = (element * 2 for element in source_sequence if element < 5)
    new_set = set(double_generator)
    
    print(new_set)
    • Skontroluj, či výstupy programu zodpovedajú predlohe. S inými textami testy neprejdú.

    Vo výstupe vidíme vygenerovanú množinu:

    Konzolová aplikácia
    {2, 4, 6, 8}

    Množina v Pythone nezachováva poradie prvkov, takže aj keď sme vytvorili množinu s hodnotami v určitom poradí, Python ich môže vrátiť v ľubovoľnom poradí. Dôležité je, že všetky správne hodnoty sú zahrnuté v množine.

    Slovníkové komprehencie

    Komprehencie je možné použiť aj na tvorbu slovníkov. Zátvorky sa používajú rovnaké ako pri množinách. Rozdiel je ten, že pre každý prvok sa namiesto jedného výrazu zapisujú dva výrazy oddelené dvojbodkou. Prvý predstavuje kľúč, druhý hodnotu. Nasledujúci kód ukazuje elegantný spôsob, ako vytvoriť kópiu slovníka, kde dôjde k prehodeniu kľúčov a hodnôt. Nutné dodať, že hodnoty zdrojového slovníka musia byť nemenného typu:

    dictionary = {"Homer": "Simpson", "Barney": "Gumble"}
    
    inverse_dictionary = {surname: name for name, surname in dictionary.items()}
    
    print(inverse_dictionary)

    Vo výstupe potom vidíme:

    Konzolová aplikácia
    {'Simpson': 'Homer', 'Gumble': 'Barney'}

    Pri hlbšom zamyslení je zrejmé, že tu chýba možnosť n-ticových komprehencií. Pre nich by sa ponúkal zápis pomocou okrúhlych zátvoriek. N-ticové komprehencie v Pythone neexistujú. Toto obmedzenie sa však dá obísť vytvorením zoznamovej komprehencie a použitím vstavanej funkcie tuple(). Kód pre n-tici párnych čísel do 20 potom vyzerá takto:

    tuple_example = tuple([x for x in range(2, 21, 2)])
    print(tuple_example)

    Vo výstupe potom vidíme:

    Konzolová aplikácia
    (2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

    Lambda výrazy

    Lambda výraz vracia funkčný objekt (je to teda iný spôsob tvorby funkcie). Vytvoríme ho kľúčovým slovom lambda. Nasledujú nepovinné parametre oddelené čiarkou a potom dvojbodka, za ktorou sa píše príslušný výraz. Ten môžeme prirovnať k telu klasickej funkcie.

    Oproti klasickým funkciám sa líšia nasledovne:

    1. Výsledná funkcia nemá meno (preto sa niekedy nazýva anonymná).
    2. Na definovanie takejto anonymnej funkcie nám musí stačiť jeden riadok (presnejšie jeden výraz).

    Druhý bod sa dá využiť napr. pri funkciách vyššieho rádu, ktoré prijímajú ako argument inú funkciu. My sa na dve z nich pozrieme na konci lekcie.

    Pozrime sa na príklad:

    def normal_function(x, y):
        return x + y
    
    lambda_function = lambda x, y: x + y
    
    f1 = normal_function(2, 5)
    f2 = lambda_function(2, 5)
    
    print(f1)
    print(f2)

    Vo výstupe vidíme, že výsledok je v oboch prípadoch rovnaký:

    Konzolová aplikácia
    7
    7

    Funkcie

    Teraz si predstavíme niekoľko vstavaných funkcií pre prácu s iterovateľnými objektmi, ktoré v prípade vhodného použitia dokážu znateľne uľahčiť prácu.

    enumerate()

    Funkcia vytvorí dvojice, kde prvou položkou je index a druhou položkou je príslušný prvok zadaného iterovateľného objektu:

    seasons = ["spring", "summer", "autumn", "winter"]
    
    enum_seasons = enumerate(seasons, start=1)
    
    for index, season in enum_seasons:
        print(index, season)

    Vo výstupe vidíme:

    Konzolová aplikácia
    1 spring
    2 summer
    3 autumn
    4 winter

    zip()

    Funkcia vytvorí n-tice zo zadaných iterovateľných objektov tak, že každá n-tica obsahuje po jednom prvku z každého iterovateľného objektu:

    movies = ['Terminator', 'Rambo', 'The Last Boy Scout', 'Titanic']
    actors = ['Schwarzenegger', 'Stallone', 'Willis', 'Di Caprio']
    years = [1984, 1982, 1991]
    
    may = zip(movies, actors, years)
    
    for i in may:
        print(i)

    Film Titanic a herec Di Caprio vo výstupe nei sú, pretože im chýba zodpovedajúci prvok z objektu years:

    Konzolová aplikácia
    ('Terminator', 'Schwarzenegger', 1984)
    ('Rambo', 'Stallone', 1982)
    ('The Last Boy Scout', 'Willis', 1991)

    map()

    Funkcia prijíma ako svoj argument inú funkciu, ktorú aplikuje na iterovateľné objekty tak, že za parametre tejto funkcie dosadia paralelne jednotlivé prvky príslušných iterovateľných objektov. To znamená, že počet parametrov funkcie sa musí rovnať počtu iterovateľných objektov.

    Príklad jednoparametrickej funkcie so zoznamom:

    prices_in_eur = [25, 1500, 10_000, 500_000]
    
    def format_price(price):
        return str(price) + ' €'
    
    prices = map(format_price, prices_in_eur)
    
    for p in prices:
        print(p)

    Výstupom je:

    Konzolová aplikácia
    25 €
    1500 €
    10000 €
    500000 €

    Príklad dvojparametrickej anonymnej funkcie s dvoma n-ticami rôznej dĺžky:

    for total in map(lambda x, y: x + y, (1, 2, 3, 4), (100, 200, 300)):
        print(total)

    Vo výstupe vidíme:

    Konzolová aplikácia
    101
    202
    303

    filter()

    Funkcia prijíma ako svoj argument inú funkciu, ktorú aplikuje na iterovateľný objekt tak, že za parameter funkcie postupne dosadzuje jednotlivé prvky tohto objektu. Ak funkcia s daným argumentom vráti False, prvok je zahodený:

    numbers = [2, 5, 13, 16, 50, 55]
    odd_numbers = filter(lambda x: x % 2, numbers)
    print(list(odd_numbers))
    Konzolová aplikácia
    [5, 13, 55]

    Kombinácia funkcií map() a filter()

    V praxi sa často stretávame s obratmi, kde sa kombinujú výhody oboch spomínaných funkcií:

    prices_in_eur = [25, 1500, 10_000, 500_000]
    
    prices_below_10_000 = map(lambda price: str(price) + "€", filter(lambda price: price < 10_000, prices_in_eur))
    
    print(list(prices_below_10_000))

    Vo výstupe vidíme:

    Konzolová aplikácia
    ['25€', '1500€']

    Rovnaký účinok však docielime pomocou zoznamovej komprehencie nasledovne:

    print([str(price) + "€" for price in prices_in_eur if price < 10_000])

    V prípade funkcie map(), ktorá pracuje s viacerými iterovateľnými objektmi, si musíme ešte vypomôcť funkciou zip():

    print(list(map(lambda x, y: x + y, (1, 2, 3, 4), (100, 200, 300))))
    print([x + y for x, y in zip((1, 2, 3, 4), (100, 200, 300))])

    Vidíme, že výstup je totožný:

    Konzolová aplikácia
    [101, 202, 303]
    [101, 202, 303]

    Použitie zoznamovej komprehencie namiesto kombinovania funkcií sa všeobecne považuje za čistejšie riešenie. Kód je priamočiarejší a lepšie čitateľný.

    V budúcej lekcii, ChainMap, NamedTuple a DeQue v Pythone, si vysvetlíme, na čo a ako sa v Pythone používajú kolekcie ChainMap, NamedTuple a DeQue.


     

    Ako sa ti páči článok?
    Pred uložením hodnotenia, popíš prosím autorovi, čo je zleZnakov 0 z 50-500
    Predchádzajúci článok
    Riešené úlohy k 4.-6. lekcii kolekcií v Pythone
    Všetky články v sekcii
    Kolekcie v Pythone
    Preskočiť článok
    (neodporúčame)
    ChainMap, NamedTuple a DeQue v Pythone
    Článok pre vás napísal synek.o
    Avatar
    Užívateľské hodnotenie:
    3 hlasov
    Aktivity