Python
Python __getitem__: что это и как работает
__getitem__ — это специальный метод Python, который вызывается, когда объект используют с квадратными скобками: obj[key] или obj[index]
Если у класса есть __getitem__, его объекты могут вести себя как список, словарь или другая коллекция с доступом по ключу
Самый простой пример
class Box:
def __init__(self, items):
self.items = items
def __getitem__(self, index):
return self.items[index]
box = Box(['a', 'b', 'c'])
print(box[0])
print(box[1])
Вывод:
a
b
Когда Python видит box[1], он фактически вызывает box.__getitem__(1)
Зачем это нужно
__getitem__ используют, когда объект должен удобно отдавать данные через квадратные скобки
Например:
- собственная коллекция
- обертка над списком
- настройки по ключу
- доступ к строкам таблицы
- объект с ленивой загрузкой данных
- библиотечный класс, который похож на список или словарь
Метод не нужен в каждом классе. Он полезен только там, где запись obj[key] действительно делает код понятнее
Доступ по ключу
Пример с настройками:
class Settings:
def __init__(self):
self.values = {
'theme': 'dark',
'language': 'ru',
}
def __getitem__(self, key):
return self.values[key]
settings = Settings()
print(settings['theme'])
print(settings['language'])
Здесь объект settings ведет себя похоже на словарь, но внутри класса можно добавить проверки, значения по умолчанию или загрузку настроек из файла
Обработка неизвестного ключа
Если ключа нет, словарь выдаст KeyError. Можно оставить такое поведение, а можно сделать сообщение понятнее
class Settings:
def __init__(self):
self.values = {'theme': 'dark'}
def __getitem__(self, key):
if key not in self.values:
raise KeyError(f'Неизвестная настройка: {key}')
return self.values[key]
settings = Settings()
print(settings['theme'])
print(settings['missing'])
Для рабочего кода важно не прятать ошибку молча. Если ключ обязателен, пусть ошибка будет явной
По этой теме полезно отдельно посмотреть Составьте еженедельное расписание занятий, чтобы расширить контекст и сравнить подходы
По этой теме полезно отдельно посмотреть Аннотации типов для декораторов в Python, чтобы расширить контекст и сравнить подходы
Поддержка срезов
Если внутри хранится список, __getitem__ может автоматически поддержать срезы
class Numbers:
def __init__(self, values):
self.values = values
def __getitem__(self, index):
return self.values[index]
numbers = Numbers([10, 20, 30, 40, 50])
print(numbers[1])
print(numbers[1:4])
Вывод:
20
[20, 30, 40]
Когда вы пишете numbers[1:4], в __getitem__ приходит объект slice
Если нужно обработать срез отдельно:
class Numbers:
def __init__(self, values):
self.values = values
def __getitem__(self, index):
if isinstance(index, slice):
return Numbers(self.values[index])
return self.values[index]
Так срез вернет новый объект Numbers, а не обычный список
__getitem__ и цикл for
Если у объекта есть __getitem__, Python может использовать его при переборе
class Box:
def __init__(self, items):
self.items = items
def __getitem__(self, index):
return self.items[index]
box = Box(['python', 'sql', 'git'])
for item in box:
print(item)
Python будет запрашивать box[0], box[1], box[2] и остановится, когда получит IndexError
Для современных коллекций часто лучше дополнительно реализовать __iter__, но пример показывает, почему __getitem__ связан с поведением коллекций
__len__ рядом с __getitem__
Если объект похож на коллекцию, рядом часто добавляют __len__
class Box:
def __init__(self, items):
self.items = items
def __getitem__(self, index):
return self.items[index]
def __len__(self):
return len(self.items)
box = Box(['a', 'b', 'c'])
print(len(box))
print(box[2])
Так объект становится понятнее для другого кода: у него можно спросить длину и взять элемент по индексу
Чем __getitem__ отличается от get()
__getitem__ — это специальный метод класса. Он отвечает за синтаксис квадратных скобок
value = obj[key]
get() — обычный метод словаря, который возвращает значение по ключу или значение по умолчанию
value = data.get('theme', 'light')
Если вы пишете свой класс, можно реализовать оба варианта, но они решают разные задачи
Частые ошибки
- добавлять
__getitem__только ради красоты, хотя обычный метод читается понятнее - возвращать
Noneпри неверном ключе и скрывать ошибку - забывать, что срез передается как
slice, а не как два числа - путать
IndexErrorдля списочного доступа иKeyErrorдля словарного доступа - делать объект похожим на список, но не добавлять
__len__ - писать слишком сложную логику внутри доступа через скобки
Практика
Сделайте класс Users, который хранит список имен
users[0]возвращает первое имяusers[1:3]возвращает часть спискаlen(users)возвращает количество пользователей- неверный индекс дает обычный
IndexError
После этого сделайте класс Settings, где settings['theme'] возвращает настройку по ключу
Что читать дальше
Для понимания классов рядом пригодится материал Классы в Python: простой пример
Если хочется увидеть больше коротких примеров, откройте Python examples
__getitem__ позволяет объекту отвечать на обращение через квадратные скобки
Он нужен для пользовательских коллекций, оберток над данными, настроек и классов, которые по смыслу похожи на список или словарь
Если скобки делают код понятнее, __getitem__ уместен. Если нет, лучше оставить обычный метод с говорящим именем