Historia pytania:
Funkcja enumerate() pojawiła się w Pythonie 2.3 i obecnie jest standardowym sposobem jednoczesnego uzyskiwania dostępu do elementu i indeksu elementu w czasie iteracji po kolekcjach. Przed pojawieniem się enumerate(), programiści często tworzyli własne liczniki lub używali funkcji range(len(sequence)), co było niewygodne i trudne do odczytania.
Problem:
Zwykła pętla for iteruje tylko po wartościach. Aby uzyskać dostęp do indeksu, często używa się range(len(...)), co nie działa dla wszystkich obiektów iterowalnych (np. generatorów, ciągów i krotek o zmiennej długości, a także podczas filtrowania). Prowadzi to do błędów i komplikuje kod.
Rozwiązanie:
enumerate() zwraca pary (indeks, element), co pozwala uzyskać indeks bieżącego elementu nawet dla niestandardowych kolekcji lub filtrowanych generatorów. Funkcja przyjmuje drugi opcjonalny argument – wartość początkową licznika.
Przykład kodu:
words = ['apple', 'banana', 'cherry'] for idx, word in enumerate(words, 1): print(f"{idx}: {word}") # Wyjście: # 1: apple # 2: banana # 3: cherry
Kluczowe cechy:
Dlaczego w funkcjach często używa się enumerate zamiast range(len(seq))?
Odpowiedź: range(len(seq)) działa tylko dla sekwencji z dostępem po indeksie i nie uwzględnia zmian długości podczas iteracji. Ponadto, jest gorzej czytelne i działa wolniej lub wcale nie działa dla generatorów. enumerate() zapewnia bezpieczny dostęp do par indeks-wartość dla dowolnej kolekcji iterowalnej.
Przykład kodu:
# Nie działa z generatorem: gen = (x for x in range(5)) for i in range(len(gen)): print(i) # Błąd: generator nie ma długości
Czy można użyć enumerate do zmiany elementów listy podczas iteracji?
Odpowiedź: Tak, ale należy iterować po indeksach, aby zapisywać wartości. W przeciwnym razie, jeśli iteruje się tylko po wartościach, zmienisz kopię obiektu, a nie oryginał.
Przykład kodu:
nums = [1, 2, 3] for idx, val in enumerate(nums): nums[idx] = val * 2 # nums = [2, 4, 6]
Co zwróci enumerate, jeśli przekażemy mu obiekt, który zmienia się w trakcie iteracji?
Odpowiedź: Jeśli kolekcja zmienia się w trakcie przeglądania (np. usuwane są elementy), zachowanie może być nieprzewidywalne, ponieważ enumerate działa na wewnętrznym iteradorze, który może się pomylić. Dlatego nie zaleca się zmieniania kolekcji podczas iteracji.
range(len(...)) dla obiektów bez długości lub dla tych, których długość może się zmienić.Negatywny przypadek
Programista przegląda listę, używając range(len(list)), i na bieżąco usuwa elementy. Rezultat — indeksy się mylą, część elementów jest pomijana.
Zalety:
Wady:
Pozytywny przypadek
Używana jest enumerate(), przy czym tworzona jest nowa lista potrzebnych elementów lub zmieniana wartość według indeksu, ale rozmiar listy nie zmienia się w pętli.
Zalety:
Wady: