@ BenoîtLatinier был прав, когда он сказал:
Dict и список не реализует __getitem__
таким же образом.
Однако я хотел бы добавить дополнительную информацию. Согласно documentation:
object.__index__(self)
Вызывается для реализации operator.index()
, и всякий раз, когда Python необходимо преобразовать без потерь числового объекта до целого объекта (, такие как в нарезке, или во встроенном в bin()
, hex()
и oct()
функции). Наличие этого метода указывает, что числовой объект является целым типом. Необходимо вернуть целое число.
Часть, выделенная жирным шрифтом, важна. Индексирование и нарезка в списке обрабатываются одним и тем же методом (а именно, __getitem__
). Таким образом, если для нарезки вызывается Thing.__index__
, он также будет вызван для индексирования, поскольку мы используем тот же метод. Это означает, что:
list_[thing]
примерно эквивалентно:
list_[thing.__index__()]
Для словаря однако Thing.__index__
не вызывается (нет никаких оснований называть его, так как вы не можете нарезать словарь). Вместо этого выполнение dict_[thing]
говорит Python, чтобы найти ключ в словаре, который является самим экземпляром thing
. Так как этого не существует, возникает KeyError
.
Возможно демонстрация будет полезно:
>>> class Thing(object):
... def __index__(self):
... print '__index__ called!'
... return 1
...
>>> thing = Thing()
>>> list_ = ['abc', 'def', 'ghi']
>>> list_[thing] # __index__ is called
__index__ called!
'def'
>>>
>>> dict_ = {1: 'potato'}
>>> dict_[thing] # __index__ is not called
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: <__main__.Thing object at 0x01ACFC70>
>>>
>>> dict_ = {thing: 'potato'} # Works if thing is a key
>>> dict_[thing]
'potato'
>>>
А почему __index__
существует в первую очередь, причина тщательно перечислены в PEP 0375. Здесь я не буду повторять все это, но в основном это так, что вы можете разрешить произвольные объекты служить целыми числами, которые необходимы для нарезки, а также для нескольких других приложений.
Для полного обоснования метода '__index__' вы можете прочитать [PEP 357] (https://www.python.org/dev/peps/pep-0357/). В принципе, '__int__' принуждает результат к целочисленному типу.Представьте, что вы написали что-то вроде 'x [3.2: 5.8]'. Это должно вызвать ошибку, и в Python это делает. Однако, если логика индексирования использовала '__int__', она бы привела значения среза к' 3' и '5', и код не вызвал бы ошибку. –