如何干净地写__getitem__?

Kev*_*vin 19 python python-3.x

在Python中,当实现序列类型时,我经常(相对而言)发现自己编写这样的代码:

class FooSequence(collections.abc.Sequence):
    # Snip other methods

    def __getitem__(self, key):
        if isinstance(key, int):
            # Get a single item
        elif isinstance(key, slice):
            # Get a whole slice
        else:
            raise TypeError('Index must be int, not {}'.format(type(key).__name__))
Run Code Online (Sandbox Code Playgroud)

代码显式检查其参数的类型isinstance().这被认为是 Python社区中的反模式.我该如何避免呢?

  • 我不能使用functools.singledispatch,因为这是非常故意与方法不兼容(它将尝试发送self,这完全没用,因为我们已经self通过OOP多态性调度).它可以使用@staticmethod,但如果我需要从中获取东西self呢?
  • 投掷int()然后捕捉TypeError,检查切片,并可能重新加注仍然是丑陋的,虽然可能稍微不那么.
  • 将整数转换为单元素切片并使用相同的代码处理这两种情况可能更清晰,但这有其自身的问题(返回0[0]?).

Sev*_*ins 14

尽管看起来很奇怪,但我怀疑你拥有它的方式是最好的方法.模式通常存在以包含常见的用例,但这并不意味着在遵循它们时应将它们视为福音,这会使生活变得更加困难.PEP 443在明确的类型检查中给出的主要原因是它"脆弱且不能延伸".但是,这主要适用于随时采用多种不同类型的自定义函数.来自Python文档__getitem__:

对于序列类型,接受的键应该是整数和切片对象.请注意,负索引的特殊解释(如果类希望模拟序列类型)取决于__getitem __()方法.如果key是不合适的类型,则可能引发TypeError; 如果序列的索引集之外的值(在对负值进行任何特殊解释之后),则应引发IndexError.对于映射类型,如果缺少键(不在容器中),则应引发KeyError.

Python文档明确说明了应该接受的两种类型,以及如果提供了不属于这两种类型的项目该怎么办.鉴于这些类型是由文档本身提供的,它不太可能改变(这样做会破坏更多的实现而不仅仅是你的实现),所以对于Python本身可能会发生变化的代码,你可能不值得花钱.

如果您打算避免明确的类型检查,我会指出您的SO答案.它包含一个@methdispatch装饰器的简洁实现(不是我的名字,但我会用它滚动),它允许@singledispatch通过强制它检查args[1](arg)而不是args[0](self)来处理方法.使用它应该允许您使用您的__getitem__方法自定义单一调度.

你是否认为这些"pythonic"中的任何一个都取决于你,但请记住,虽然Python的Zen指出"特殊情况不足以打破规则",但它立即注意到"实用性超越纯度" .在这种情况下,只检查文档明确指出的两种类型是唯一__getitem__应该支持的东西对我来说似乎是实用的方法.