Python __index__ special method

前端 未结 3 1981
南旧
南旧 2021-01-01 19:47
>>> class Thing(object):
...     def __index__(self):
...         return 1
... 
>>> thing = Thing()
>>> list_ = [\'abc\', \'def\', \'ghi\'         


        
3条回答
  •  心在旅途
    2021-01-01 20:23

    @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 "", line 1, in 
    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.

提交回复
热议问题