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, который хранит список имен

  1. users[0] возвращает первое имя
  2. users[1:3] возвращает часть списка
  3. len(users) возвращает количество пользователей
  4. неверный индекс дает обычный IndexError

После этого сделайте класс Settings, где settings['theme'] возвращает настройку по ключу

Что читать дальше

Для понимания классов рядом пригодится материал Классы в Python: простой пример

Если хочется увидеть больше коротких примеров, откройте Python examples

__getitem__ позволяет объекту отвечать на обращение через квадратные скобки

Он нужен для пользовательских коллекций, оберток над данными, настроек и классов, которые по смыслу похожи на список или словарь

Если скобки делают код понятнее, __getitem__ уместен. Если нет, лучше оставить обычный метод с говорящим именем

Подписаться
Уведомить о
guest
0 комментариев
Старые
Новые Популярные

Настройки cookie

0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x