【问题标题】:Python __index__ special methodPython __index__ 特殊方法
【发布时间】:2015-02-11 11:58:09
【问题描述】:
>>> class Thing(object):
...     def __index__(self):
...         return 1
... 
>>> thing = Thing()
>>> list_ = ['abc', 'def', 'ghi']
>>> list_[thing]
'def'
>>> dict_ = {1: 'potato'}
>>> dict_[thing]
# KeyError

thing 如何知道在被列表访问时将自己表示为 1,而不是被字典访问?两种魔术方法都不通过__getitem__吗?列表中显示的用法可以通过 __int__ 而不是,那么 __index__存在理由是什么?

【问题讨论】:

  • 有关__index__ 方法的完整推理,您可以阅读PEP 357。基本上,__int__ 将结果强制转换为整数类型。想象一下,如果你写了类似x[3.2:5.8] 的东西。这应该会引发错误,而在 Python 中确实会引发错误。但是,如果索引逻辑使用__int__,它会将切片值强制为35,并且代码不会引发错误。

标签: python magic-methods


【解决方案1】:

@BenoîtLatinier 说得对:

Dict 和 List 实现 __getitem__ 的方式不同。

不过,我想补充一点信息。根据documentation

object.__index__(self)

调用实现operator.index(),每当Python需要将数字对象无损转换为整数对象时(比如 在切片中,或者在内置的bin()hex()oct() 职能)。此方法的存在表明数字对象 是整数类型。必须返回一个整数。

我加粗的部分很重要。列表上的索引和切片都由相同的方法处理(即__getitem__)。因此,如果调用Thing.__index__ 进行切片,则同样会调用它进行索引,因为我们使用的是相同的方法。这意味着:

list_[thing]

大致相当于:

list_[thing.__index__()]

但是,对于字典,Thing.__index__ 没有被调用(没有理由调用它,因为您无法对字典进行切片)。相反,dict_[thing] 是告诉 Python 在字典中找到一个键,即 thing 实例本身。由于这不存在,因此引发了 KeyError

也许演示会有所帮助:

>>> 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'
>>>

至于为什么__index__首先存在,原因在PEP 0375中详细列出。我不会在这里重复所有这些,但基本上它是为了让您可以允许任意对象用作整数,这在切片以及其他一些应用程序中是必需的。

【讨论】:

    【解决方案2】:

    Dict 和 List 实现 __getitem__ 的方式不同。 Dict 对象使用对象的 __hash__ 上的比较 (__eq__) 作为在 __getitem__ 中使用的键。

    要使Thing 可用于 dict,您必须同时实现 hash 和 eq。

    【讨论】:

      【解决方案3】:

      另一个进一步理解的例子,这里_MolsToGridSVG 接受一个列表参数。我想将列表限制在一定长度。现在在这里对于 python 列表,必须使用切片索引。以下实现解决了它。基本上这里的索引被用于 Python 列表。

      def __index__(self):
          return 1
      
      imagesInOneFile = int(len(smilesMolList) / noOfFiles)
      svg = Draw._MolsToGridSVG(smilesMolList[:imagesInOneFile.__index__()], subImgSize=(400, 200), molsPerRow=2)
      

      还需要记住imagesInOneFile 必须是整数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-05-14
        • 2019-03-16
        • 1970-01-01
        • 2016-11-29
        • 1970-01-01
        • 2010-11-08
        • 1970-01-01
        • 2010-12-26
        相关资源
        最近更新 更多