Python __index__ special method

可紊 提交于 2019-11-29 17:14:25

问题


>>> 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-in bin(), hex() and oct() 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!