Python __index__ special method
@BenoîtLatinier was correct when he said:
Dict and List does not implement
__getitem__
the same way.
However, I'd like to add a little more information. According to the documentation:
object.__index__(self)
Called to implement
operator.index()
, and whenever Python needs to losslessly convert the numeric object to an integer object (such as in slicing, or in the built-inbin()
,hex()
andoct()
functions). Presence of this method indicates that the numeric object is an integer type. Must return an integer.
The part I bolded is important. Indexing and slicing on a list are both handled by the same method (namely, __getitem__
). So, if Thing.__index__
is called for slicing, it will likewise be called for indexing since we are using the same method. This means that:
list_[thing]
is roughly equivalent to:
list_[thing.__index__()]
For the dictionary however, Thing.__index__
is not being called (there is no reason to call it since you cannot slice a dictionary). Instead, doing dict_[thing]
is telling Python to find a key in the dictionary that is the thing
instance itself. Since this doesn't exist, a KeyError
is raised.
Perhaps a demonstration will be helpful:
>>> 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 calledTraceback (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'>>>
As for why __index__
exists in the first place, the reason is thoroughly listed in PEP 0375. 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.
Dict and List does not implement __getitem__
the same way. Dict objects uses a comparison (__eq__
) on __hash__
of objects as key to use in __getitem__
.
To make Thing
usable for dict you have to implement both hash and eq.
Another example to understand it further, here _MolsToGridSVG takes a list argument. I wanted to limit the list to some length. Now here for python list, slice indices have to be used. The following implementation solved it. Basically here index is getting used for Python List.
def __index__(self): return 1imagesInOneFile = int(len(smilesMolList) / noOfFiles)svg = Draw._MolsToGridSVG(smilesMolList[:imagesInOneFile.__index__()], subImgSize=(400, 200), molsPerRow=2)
Also one needs to remember that imagesInOneFile
has to be integer.