问题
>>> 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?
回答1:
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.
回答2:
@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 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'
>>>
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.
回答3:
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 1
imagesInOneFile = 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.
来源:https://stackoverflow.com/questions/27449310/python-index-special-method