Historia pytania:
Sekwencje to jedna z najstarszych i najbardziej podstawowych koncepcji w Pythonie. Klasyczne przykłady to listy (list), łańcuchy (str) i krotki (tuple). Do interakcji z sekwencjami wprowadzono specjalny protokół: konieczne jest zaimplementowanie metod __getitem__ i __len__. Dzięki temu obiekt zachowuje się "jak sekwencja" — wspiera indeksowanie, wycinki, pracę w pętlach i niektóre standardowe funkcje.
Problem:
Bez właściwej implementacji tych metod, klasa użytkownika nie będzie mogła działać w operacjach indeksowania, w pętli for oraz w funkcjach takich jak len(). Często początkujący programiści implementują tylko jedną z metod, nie biorąc pod uwagę obsługi wyjątków, nie implementują wsparcia dla wycinków (slice), co prowadzi do nieprawidłowego lub nieoczekiwanego zachowania.
Rozwiązanie:
Należy zaimplementować metodę __getitem__(self, key) do wsparcia indeksacji i wycinków, a także __len__(self) do używania funkcji len() i prawidłowej iterowalności. Aby wspierać wycinki, należy rozróżniać typ key w __getitem__ i poprawnie obsługiwać obiekty slice.
Przykład kodu:
def is_even(n): return n % 2 == 0 class EvenSequence: def __init__(self, size): self.size = size def __getitem__(self, index): if isinstance(index, slice): return [x for x in range(self.size)[index] if is_even(x)] if index < 0 or index >= self.size: raise IndexError('Index poza zakresem') return index if is_even(index) else None def __len__(self): return self.size
Kluczowe cechy:
Czy można ograniczyć się tylko do metody getitem, aby obiekt działał jako sekwencja?
Częściowo. Jeśli zaimplementuje się tylko __getitem__, można będzie iterować przez obiekt za pomocą for i indeksować elementy, ale len() nie będzie działać.
Przykład kodu:
class SeqOnlyGetitem: def __getitem__(self, index): if index >= 10: raise IndexError return index * 2 s = SeqOnlyGetitem() for x in s: print(x) # Działa (iterowalne) # print(len(s)) # Błąd TypeError
Jak obsługiwać indeksy ujemne i co się stanie, jeśli ich nie uwzględnimy?
Indeksy ujemne to kluczowa cecha sekwencji w Pythonie. Jeśli ich nie obchodzić, obiekt zachowuje się w sposób nieoczekiwany dla użytkownika.
class NegIndex: def __init__(self, data): self.data = data def __getitem__(self, index): if index < 0: index += len(self.data) return self.data[index]
Czy należy implementować contains lub iter dla swojej klasy Sequence?
Nie jest to konieczne. Jeśli zaimplementowane są __getitem__ i __len__, funkcja in będzie działać, a for będzie je wykorzystywać do iteracji. Implementacja tych metod zwykle nie jest wymagana, ale może poprawić wydajność.
Programista zaimplementował tylko getitem dla swojej klasy tylko dla dodatnich indeksów. Moduł przeszedł część testów jednostkowych, ale przy rzeczywistym użyciu, próbując wziąć nieistniejący indeks lub indeks ujemny, wszystko się łamało.
Zalety:
Wady:
Zespół zaimplementował obie metody (getitem i len), uwzględnił wycinki, indeksy ujemne, wyrzucał poprawne wyjątki. Końcowa klasa działała we wszystkich standardowych przypadkach Pythona.
Zalety:
Wady: