Enumerate w Python


Dowiedz się jak szybko nakładać indeks na iterowalne obiekty w Python przy użyciu enumerate.


Enumerate to jedna z wielu funkcji wbudowanych w Python. Jest bardzo użyteczna ale mimo to wielu początkujących i nawet czasem bardziej zaawansowanych programistów o niej zapomina. Nawet dziś miałem sytuację w której w pracy właśnie enumerate w Python bardzo uprościł nam kod. Jeśli jesteś ciekaw czym właściwie jest enumerate i jak działa, zapraszam do lektury.

We wpisie z poradami do Python na pierwszym miejscu, znalazł się właśnie enumerate. Przypomnę tylko jak prawdopodobnie wygląda twój kod jeśli pisałeś wcześniej w innych językach i dopiero co przesiadłeś się na Python.

countries = ['Polska', 'Niemcy', 'Grecja', 'Szwecja', 'Rosja']

i = 0
for country in countries:
    print(i, country)
    i += 1

W tym kodzie nie ma nic złego ale jak już używamy języka to korzystajmy z jego pełnego potencjału. I dlatego właśnie warto pamiętać o enumerate, czyli funkcji która pozwala nałożyć indeks na obiekt po którym iterujemy.

Enumerate i lista

Dlatego właśnie prostym zabiegiem można ten kod znacznie uprościć.

countries = ['Polska', 'Niemcy', 'Grecja', 'Szwecja', 'Rosja']

for i, country in enumerate(countries):
    print (i, country)

Ten prosty zabieg pozwolił nam skrócić kod sprawiając tym, że jest bardziej " Pythonowowy ". Enumerate można nałożyć na każdy obiekt, po którym można iterować. Od list i tupli, po iteratory i generatory.

Enumerate i tupla

Wyżej pokazałem jak zachowuje się enumerate z listą ale nic nie stoi na przeszkodzie, żeby użyć go również z tuplą. Zobaczmy jak można to zaimplementować

countries = ('Polska', 'Niemcy', 'Grecja', 'Szwecja', 'Rosja')

for i, country in enumerate(countries):
    print (i, country)

Enumerate i złożone struktury

Czasem zachodzi potrzeba iterowania po zagnieżdżonych listach lub innych rozbudowanych strukturach. Z tego samego wpisu dotyczącego porad w Python wiemy jak łatwo rozpakować obiekty w Python. Zobaczmy zatem jak można to fajnie połączyć z enumerate. Możemy wypisać zagnieżdżone obiekty jak niżej

persons = [('Dawid', 25), ('Jan', 23), ('Marcin', 22)]

for i, person in enumerate(persons):
    print(i, ":", person)

Możemy też rozpakować obiekty i to wszystko połączyć z enumerate. Będzie to wymagać małego zabiegu ale da się

persons = [('Dawid', 25), ('Jan', 23), ('Marcin', 22)]

for i, (name, age) in enumerate(persons):
    print(i, ".", name, 'age', age)

Enumerate z generatorem

Jak mowa o iterowalnych obiektach to nie może tu zabraknąć generatorów. Zbudujmy odpowiednik range i sprawdźmy jak się zachowa.

def gen(n):
    num = 0
    while num < n:
        yield num
        num += 1

for i, num in enumerate(gen(10)):
    print(i, num)

Enumerate z iteratorem

Skoro były generatory, to czas na iterator :)

class IncrementIterator:

    def __init__(self, n):
        self.n = n
        self.i = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.n == self.i:
            raise StopIteration

        self.i += 1
        return self.i


for i, num in enumerate(IncrementIterator(3)):
    print(i, num)

Enumerate i string

Zapewne nie jest dla Ciebie nowością, że można iterować po ciągach znaków ale czy wiedziałeś, że na to również można założyć enumerate? Sprawdź jak można to wykorzystać w praktyce

name = 'DDeby'
for i, letter in enumerate(name):
    print(letter, 'jest', i, 'literą w tekście', name)

Enumerate z parametrem start

W programowaniu indeksowanie zaczynamy od 0, w życiu natomiast od 1. Na to również jest bardzo prosty sposób, wystarczy bowiem dodać drugi parametr do naszej funkcji. O ile sam enumerate jest jeszcze wykorzystywane o tyle z parametrem start jest już gorzej.

Zobaczymy zatem jak poprawić powyższy kod, żeby wypisywał wyniki w nieco bardziej przystępnej formie

names = ['Dawid', 'Jan', 'Marcin']
for i, name in enumerate(names, start=1):
    print(i, '.', name)

Kontekst zimennej

Zastosowanie enumerate ma jeszcze jedną zaletę, mianowicie zmienna "i" jest już zadeklarowana. Co nam to daje? Sporo, bo mając tę zmienną nie musimy ponownie sprawdzać wielkości listy. Łatwiej zrozumiemy to na przykładzie. Jak wypisać podsumowanie bez wykorzystania tego efektu zobaczmy niżej

names = ['Dawid', 'Jan', 'Marcin']
for i, name in enumerate(names, start=1):
    print(i, '.', name)

print('Zgłosiło się ', len(names), 'uczestników')

Po co sprawdzać długość listy, skoro wiemy ile przebiegów pętli zrobiliśmy

names = ['Dawid', 'Jan', 'Marcin']
for i, name in enumerate(names, start=1):
    print(i, '.', name)

print('Zgłosiło się ', i, 'uczestników')

Na małych listach nie odczujemy różnicy ale przy większych zbiorach lub przy odpytywaniu bazy już jak najbardziej.

Enumerate a pusta lista

W powyższym przykładzie jest jeden haczyk. Ten kod nie zadziała jeśli lista będzie pusta. Nie zadziała, bo jeśli nie będzie obiektów po których można iterować to zmienna do inkrementowania nie będzie zadeklarowana i niżej zostanie wyrzucony wyjątek.

names = []
for i, name in enumerate(names, start=1):
    print(i, '.', name)

print('Zgłosiło się ', i, 'uczestników')  # NameError: name 'i' is not defined

Podsumowanie

Właśnie poznałeś ciekawe zastosowanie enumerate w Python. Wiesz już jak są zagrożenia i możliwości tej instrukcji.

Jeśli wcześniej nie znałeś takich zastosowań albo znasz kogoś, komu może się przydać, koniecznie podaj ten wpis dalej. A może znasz jakieś inne jego ciekawe zastosowanie? Koniecznie podziel się tym w komentarzu.

Paź 26, 2019

Najnowsze wpisy

Zobacz wszystkie