programowanieProgramista bibliotek Python

Jak działa metoda __getitem__ w Pythonie, po co ją implementować i na co zwrócić uwagę przy obsłudze wycinków (slice)?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Historia pytania

Metoda getitem została dodana do Pythona jako część protokołu sekwencji i map. Za pomocą tej magicznej metody standardowe kolekcje (list, tuple, dict itd.) wspierają dostęp przez indeks, klucz i wycinek. W klasach użytkowników ta metoda pozwala obiektom zachowywać się "jak kolekcje".

Problem

Bez implementacji getitem klasa użytkownika nie wspiera indeksowania i iteracji przez for. Implementacja tylko dla prostych indeksów jest nieodpowiednia, jeśli chcemy pełnej obsługi wycinków (slice), a ignorowanie rodzajów indeksów prowadzi do błędów.

Rozwiązanie

Zaimplementować getitem do wsparcia zarówno indeksów, jak i obiektów typu slice, rozdzielając je w obróbce. Pozwala to na użycie obiektów użytkownika w standardowych konstrukcjach Pythona (np. do wsparcia wycinków your_obj[1:5]).

Przykład kodu:

class MyRange: def __init__(self, n): self.n = n def __getitem__(self, item): if isinstance(item, int): # Indywidualny indeks if 0 <= item < self.n: return item * 2 raise IndexError('index out of range') elif isinstance(item, slice): # Wycinek return [self[i] for i in range(*item.indices(self.n))] else: raise TypeError('Invalid argument type: {}'.format(type(item))) mr = MyRange(10) print(mr[3]) # 6 print(mr[2:5]) # [4, 6, 8]

Kluczowe cechy:

  • getitem — podstawa protokołu sekwencji i iterowalności
  • Aby wspierać wycinki, trzeba obsługiwać slice oddzielnie
  • Wyjątki IndexError i TypeError są potrzebne do poprawnej pracy standardowych funkcji (np. list(), for)

Pytania z pułapką.

Czy implementacja tylko getitem pozwala na przypisywanie wartości przez indeks?

Nie. Aby wspierać przypisywanie (your_obj[i] = value), trzeba zaimplementować setitem. getitem odpowiada tylko za odczyt.

Czy getitem musi zwracać listę na wycinku, jak list?

Nie. Najważniejsze — zwracać "sekwencję" z uwzględnieniem sensu klasy (można zwracać ten sam typ lub, na przykład, tuple). Ważne, aby miało to sens w kontekście zadania.

Dlaczego czasami pojawia się błąd TypeError: 'MyClass' object is not subscriptable?

Taki komunikat pojawia się, jeśli próbujesz wykonać my_obj[0], a klasa nie implementuje getitem. Aby klasa była subscriptable (wspierała []), ta metoda jest obowiązkowa.

Typowe błędy i antywzorce

  • Sprawdzają tylko int, ignorując slice: wycinki zawsze prowadzą do błędu
  • Zwracają niewłaściwy typ (np. None przy wycinkach zamiast sekwencji)
  • Nie wyrzucają IndexError, przez co pętla for działa nieprawidłowo

Przykład z życia

Negatywny przypadek

Zaimplementowano getitem tylko dla int, zapomniano o slice. Każda próba my_obj[2:5] prowadzi do TypeError i awarii całego algorytmu przetwarzania kolekcji.

Zalety:

  • Prostota

Wady:

  • Nie wspierają wycinków, kod niekompatybilny z większością standardowych konstrukcji

Pozytywny przypadek

getitem zaimplementowany z oddzielną obsługą slice i int. Wycinki i indeksy działają, metody list(), map(), iteracje są wspierane bez dodatkowego wysiłku.

Zalety:

  • Klasa jest kompatybilna ze standardowymi narzędziami Pythona
  • Łatwo rozszerza się na dowolne typy indeksów (tuple, str dla macierzy itd.)

Wady:

  • Implementacja wymaga nieco więcej kodu i testowania