programowanieProgramista Backend

Czym jest obiekt iterowalny w Pythonie i jak prawidłowo zaimplementować niestandardowy obiekt iterowalny?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

Historia pytania: Pojęcie iterowalności pojawiło się w Pythonie w celu ujednolicenia pracy z kolekcjami: listami, słownikami, zbiorami itp. Obiekt, po którym można przejść za pomocą pętli for, uznawany jest za iterowalny. Jest to realizowane za pomocą określonych metod magicznych.

Problem: Python wymaga określonych protokołów do poprawnej pracy pętli i funkcji związanych z sekwencjami. Jeśli użytkownik niewłaściwie zaimplementuje te protokoły w swojej klasie, standardowe mechanizmy (for, list(), sum() itd.) nie będą działać lub będą się zachowywać w sposób nieprzewidywalny.

Rozwiązanie: Iterowalnym obiektem jest każdy obiekt, który implementuje metodę __iter__. Iterator to ten, który ma metodę __next__ oraz __iter__, zwracającą self. Zwykle obiekt zwracany przez __iter__ jest iteratorem, ale nie jest to konieczne. Przykład:

class MyRange: def __init__(self, start, end): self.start = start self.end = end def __iter__(self): self.current = self.start return self def __next__(self): if self.current < self.end: val = self.current self.current += 1 return val raise StopIteration for x in MyRange(1, 4): print(x) # 1, 2, 3

Kluczowe cechy:

  • Iterowalność określa obecność metody __iter__.
  • Iterator musi implementować zarówno __next__, jak i __iter__ (zwracający self).
  • Zwracanie nowego obiektu w __iter__ efektywnie realizuje się, jeśli wymagane są niezależne przejścia przez kolekcję.

Pytania z pułapką.

Czy metoda next jest wymagana dla każdego obiektu iterowalnego?

Nie. Dla obiektu iterowalnego wymagana jest tylko __iter__, zwracająca iterator. __next__ implementuje tylko sam iterator. Na przykład, lista nie ma metody __next__, ale jest iterowalna: jej __iter__ zwraca instancję iteratora.

lst = [1, 2, 3] print(hasattr(lst, '__next__')) # False

Czy obiekt może być samodzielnym iteratorem?

Tak, jeśli implementuje obie metody — zarówno __iter__, jak i __next__.

Czy można stworzyć kilka iteratorów z niezależnym stanem dla jednej kolekcji?

Tak, jeśli __iter__ zwraca za każdym razem nowy obiekt iteratora.

class MyList: def __init__(self, data): self.data = data def __iter__(self): return iter(self.data)

Typowe błędy i antywzorce

  • Implementacja tylko __next__ bez __iter__ (lub odwrotnie).
  • Zapamiętywanie stanu na poziomie klasy, a nie instancji, co prowadzi do błędów przy kolejnych przejściach.

Przykład z życia

Negatywny przypadek: Klasa iteratora przechowuje stan (na przykład, aktualny indeks) na poziomie klasy, a nie instancji, co powoduje, że równoległe przejścia się ze sobą kolidują. Zalety:

  • Mniej pamięci (tylko jeden indeks). Wady:
  • Błędy podczas jednoczesnej iteracji dwoma pętlami.

Pozytywny przypadek: Każdy iterator przechowuje swój stan w instancji utworzonej w __iter__. Zalety:

  • Poprawna praca kilku iteratorów. Wady:
  • Nieznacznie większy zużycie pamięci.