W Pythonie protokół 'Sequence' definiuje interfejs dla obiektów, które można indeksować i iterować, takich jak listy lub krotki. Historycznie, sekwencje pojawiły się praktycznie od pierwszych wersji Pythona, aby wspierać naturalne operacje na strukturach danych: indeksowanie, wycinki, iteracja elementów.
Problem — aby klasa użytkownika zachowywała się jak sekwencja, samo zaimplementowanie metod iter i next nie wystarczy. Aby w pełni wspierać zachowanie sekwencji, niezbędne są dodatkowe metody.
Rozwiązanie — aby zaimplementować własny typ sekwencji, należy zdefiniować metody getitem (niezbędna do indeksowania i wycinków) i opcjonalnie len (do len() i sprawdzania długości). W ten sposób obiekt będzie wspierać iterację, dostęp po indeksie, pracę z wycinkami, a także wiele standardowych operacji Pythona z sekwencjami.
Przykład kodu:
class MyCounter: def __init__(self, stop): self._stop = stop def __getitem__(self, index): if 0 <= index < self._stop: return index * 10 else: raise IndexError('Poza zakresem') def __len__(self): return self._stop c = MyCounter(5) print(c[3]) # 30 print(len(c)) # 5 for x in c: print(x)
Kluczowe cechy:
Czy jeśli zaimplementuję tylko iter i next, mój obiekt stanie się sekwencją (Sequence)?
Nie. Taki obiekt będzie tylko iterowalnym (iterable), ale nie sekwencją. Nie będzie wspierać indeksowania, wycinków ani standardowych funkcji podobnych do list.
Czy konieczne jest zaimplementowanie getitem do wsparcia pętli for?
Nie jest to konieczne. Jeśli została zaimplementowana iter, for będzie działać. Ale jeśli nie ma iter, interpreter spróbuje użyć getitem, zaczynając od indeksu 0, aż napotka IndexError. Dlatego dla sekwencji wystarczy getitem.
Czy można zaimplementować getitem tylko dla int, a nie dla slice?
Technicznie obiekt będzie działał dla c[0], ale próba wzięcia wycinka c[1:4] nie zadziała. Aby wspierać wycinki, getitem musi umieć obsługiwać obiekty typu slice (zobacz slice.indices i isinstance(key, slice)).
Przykład kodu:
class S: def __getitem__(self, idx): if isinstance(idx, slice): return [x for x in range(idx.start or 0, idx.stop or 10, idx.step or 1)] return idx * 2
Zaimplementowano niestandardową strukturę, definiując tylko iter, myśląc, że teraz można używać wycinków i indeksacji.
Zalety:
Wady:
Klasa implementuje getitem z obsługą pracy z wycinkami i indeksami int, a także len.
Zalety:
Wady: