>>> class Thing(object):
... def __index__(self):
... return 1
...
>>> thing = Thing()
>>> list_ = ['abc', 'def', 'ghi']
>>> list_[thing]
'def'
>>> dict_ = {1: 'potato'}
>>> dict_[thing]
# KeyError
How does thing
know to represent itself as 1 when accessed by a list, but not by a dict? Don't both magic methods go through __getitem__
? The usage shown for lists could go through __int__
instead so what is the raison d'être for __index__
anyway?
Best Answer
@BenoîtLatinier was correct when he said:
However, I'd like to add a little more information. According to the documentation:
The part I bolded is important. Indexing and slicing on a list are both handled by the same method (namely,
__getitem__
). So, ifThing.__index__
is called for slicing, it will likewise be called for indexing since we are using the same method. This means that:is roughly equivalent to:
For the dictionary however,
Thing.__index__
is not being called (there is no reason to call it since you cannot slice a dictionary). Instead, doingdict_[thing]
is telling Python to find a key in the dictionary that is thething
instance itself. Since this doesn't exist, aKeyError
is raised.Perhaps a demonstration will be helpful:
As for why
__index__
exists in the first place, the reason is thoroughly listed in PEP 0357. I won't repeat all of it here, but basically it is so that you can allow arbitrary objects to serve as integers, which is needed in slicing as well as a few other applications.