История вопроса:
Последовательности — одна из древнейших и наиболее базовых концепций в Python. Классические примеры — списки (list), строки (str), кортежи (tuple). Для взаимодействия с последовательностями был выделен специальный протокол: необходимо реализовать методы __getitem__ и __len__. Это позволяет объекту вести себя "как последовательность" — поддерживать индексирование, срезы, работу в циклах и даже некоторые стандартные функции.
Проблема:
Без правильной реализации этих методов пользовательский класс не сможет работать с операциями индексирования, for-циклом, функциями вроде len(). Часто начинающие разработчики реализуют только один из методов, не учитывают обработку исключений, не реализуют поддержку срезов (slice) и получают некорректное или неожиданное поведение.
Решение:
Нужно реализовать метод __getitem__(self, key) для поддержки индексации и срезов, а также __len__(self) для работы с функцией len() и корректной итерируемости. Для поддержки срезов нужно различать тип key в __getitem__ и корректно обрабатывать объекты slice.
Пример кода:
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 out of bounds') return index if is_even(index) else None def __len__(self): return self.size
Ключевые особенности:
Можно ли ограничиться только методом getitem, чтобы объект работал как последовательность?
Частично. Если реализовать только __getitem__, то по объекту можно будет итерироваться через for и индексировать элементы, но len() не заработает.
Пример кода:
class SeqOnlyGetitem: def __getitem__(self, index): if index >= 10: raise IndexError return index * 2 s = SeqOnlyGetitem() for x in s: print(x) # Работает (итерируемо) # print(len(s)) # Ошибка TypeError
Как обработать отрицательные индексы и что будет, если их не учитывать?
Отрицательные индексы — ключевая особенность последовательностей в Python. Если их не обрабатывать, объект ведет себя неожиданно для пользователя.
class NegIndex: def __init__(self, data): self.data = data def __getitem__(self, index): if index < 0: index += len(self.data) return self.data[index]
Нужно ли реализовывать contains или iter для своего Sequence-класса?
Не обязательно. Если реализованы __getitem__ и __len__, функция in будет работать, а for будет использовать их для итерации. Реализация этих методов напрямую обычно не нужна, но может повысить производительность.
Разработчик реализовал только getitem для своего класса только для положительных индексов. Модуль прошел часть unit-тестов, но при реальном использовании при попытке взять несуществующий индекс или отрицательный индекс всё ломалось.
Плюсы:
Минусы:
Команда реализовала оба метода (getitem и len), учла срезы, отрицательные индексы, выбрасывала корректные исключения. Итоговый класс работал во всех стандартных случаях Python.
Плюсы:
Минусы: