Pętla for-in w Pythonie jest realizowana za pomocą protokołu iteracji, opartego na dwóch specjalnych metodach: __iter__() i __next__(). Gdy pętla się zaczyna, Python wywołuje iter(obj) (szuka metody __iter__). Jeśli zwracany jest obiekt-iterator, w którym zdefiniowano __next__(), pętla zaczyna wywoływać tę metodę, aby uzyskać każdy następny element.
Jeśli element zostaje pomyślnie zwrócony — iteracja kontynuuje. Gdy zostaje zgłoszone wyjątek StopIteration, pętla kończy działanie.
Przykład własnego iteratora:
class Counter: def __init__(self, low, high): self.current = low self.high = high def __iter__(self): return self def __next__(self): if self.current > self.high: raise StopIteration else: self.current += 1 return self.current - 1 for num in Counter(1, 3): print(num) # Wyświetli 1, 2, 3
Szczegół: Wiele standardowych struktur w Pythonie implementuje protokół iteratora; łatwo możesz zrobić swoje obiekty takie same.
Czy można używać pętli for-in z dowolnym obiektem Pythonu? Czym różni się obiekt iterowalny od iteratora?
Odpowiedź: Nie, pętla for-in wymaga, aby obiekt był iterowalny, tzn. implementował metodę __iter__(), która zwraca iterator (obiekt z metodą __next__()). Sam iterator zwraca siebie przez __iter__(), a obiekt iterowalny — nie.
Przykład:
lst = [1, 2, 3] iterator = iter(lst) print(hasattr(lst, '__iter__')) # True print(hasattr(iterator, '__next__')) # True (iterator) print(hasattr(lst, '__next__')) # False (nie iterator)
Historia
Projekt: Przetwarzanie dużych plików.
Problem: W projekcie próbowano powtórzyć iterację po obiekcie pliku (iteracja po liniach pliku) dwukrotnie bez jawnego otwierania nowego pliku — i przy drugiej iteracji nie otrzymano żadnej linii (wskaźnik już znajdował się na końcu pliku!).
Historia
Projekt: Integracja własnej klasy kolekcji z frameworkiem.
Problem: Klasa zaimplementowała tylko
__getitem__, ale nie__iter__, i nie była zgodna z generatorami oraz standardowymi pętlami (for). Z tego powodu łamały się zewnętrzne biblioteki, które oczekiwały iteratora.
Historia
Projekt: Użycie map/filter na niestandardowych kolekcjach.
Problem: Własna klasa listy nie zaimplementowała
__iter__i__next__, przez co okazała się niezgodna z wieloma standardowymi funkcjami Pythona, co ujawniło się dopiero po testach integracyjnych i wymagało znacznej przeróbki.