Python 如何清晰地编写uu getitem uuu?
在Python中,当实现序列类型时,我经常(相对而言)发现自己在编写这样的代码:Python 如何清晰地编写uu getitem uuu?,python,python-3.x,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):
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__))
代码使用isinstance()
显式检查其参数的类型。这是在Python社区中实现的。我如何避免它
- 我不能使用
,因为这与方法不兼容(它将尝试在functools.singledispatch
上进行调度,这是完全无用的,因为我们已经通过OOP多态性在self
上进行调度)。它与self
一起工作,但是如果我需要从@staticmethod
中获取内容,该怎么办self
- 强制转换到
,然后捕获int()
,检查一个片段,并可能重新提升,这仍然很难看,尽管可能稍微不那么难看TypeError
- 将整数转换为一个元素片段并用相同的代码处理这两种情况可能会更简洁,但这也有其自身的问题(返回
或0
?)[0]
@methdispatch
装饰器实现(不是我的名字,但我会使用它),它允许@singledispatch
通过强制它检查args[1]
(arg)而不是args[0]
(self)来使用方法。使用它应该允许您在\uuu getitem\uuu
方法中使用自定义的单一分派
你是否认为这些“Python”取决于你,但请记住,虽然Python的禅指出“特殊情况并不特别足以打破规则”,但它立刻注意到“实用性胜过纯粹性”。在这种情况下,仅检查文档明确指出的两种类型是
\uuu getitem\uuuu
应该支持的唯一内容,对我来说似乎是一种切实可行的方法。我不知道有什么方法可以避免这样做一次。这只是以这种方式使用动态类型语言的折衷。然而,这并不意味着你必须一次又一次地这样做。我将通过创建一个带有拆分方法名的抽象类来解决这个问题,然后从该类继承,而不是直接从序列继承,如:
class UnannoyingSequence(collections.abc.Sequence):
def __getitem__(self, key):
if isinstance(key, int):
return self.getitem(key)
elif isinstance(key, slice):
return self.getslice(key)
else:
raise TypeError('Index must be int, not {}'.format(type(key).__name__))
# default implementation in terms of getitem
def getslice(self, key):
# Get a whole slice
class FooSequence(UnannoyingSequence):
def getitem(self, key):
# Get a single item
# optional efficient, type-specific implementation not in terms of getitem
def getslice(self, key):
# Get a whole slice
这清理了FooSequence
,如果我只有一个派生类,我甚至可以这样做。我有点惊讶标准库并没有这样工作。为了保持pythonic,您必须处理语义而不是对象的类型。所以,如果您有一些参数作为序列的访问器,就这样使用它。尽可能长时间地使用参数的抽象。如果需要一组用户标识符,请不要使用一组,而是使用方法add
的一些数据结构。如果需要一些文本,不要使用unicode
对象,而是使用encode
和decode
方法的字符容器
通常,我假设您希望执行类似“使用基本实现的行为,除非提供了某些特殊值。如果您希望实现\uuuu getitem\uuuu
,则可以使用案例区分,如果提供了一个特殊值,则会发生不同的情况。我将使用以下模式:
class FooSequence(collections.abc.Sequence):
# Snip other methods
def __getitem__(self, key):
try:
if key == SPECIAL_VALUE:
return SOMETHING_SPECIAL
else:
return self.our_baseclass_instance[key]
except AttributeError:
raise TypeError('Wrong type: {}'.format(type(key).__name__))
如果您想区分单个值(在perl术语“标量”中)和序列(在Java术语“集合”中),那么确定是否实现了迭代器就可以从pythonical角度进行判断。您可以使用try-catch模式或hasattr
,就像我现在所做的那样:
>>> a = 42
>>> b = [1, 3, 5, 7]
>>> c = slice(1, 42)
>>> hasattr(a, "__iter__")
False
>>> hasattr(b, "__iter__")
True
>>> hasattr(c, "__iter__")
False
>>>
适用于我们的示例:
class FooSequence(collections.abc.Sequence):
# Snip other methods
def __getitem__(self, key):
try:
if hasattr(key, "__iter__"):
return map(lambda x: WHATEVER(x), key)
else:
return self.our_baseclass_instance[key]
except AttributeError:
raise TypeError('Wrong type: {}'.format(type(key).__name__))
python和ruby等动态编程语言使用duck类型。duck是一种动物,走路像鸭子,游泳像鸭子,嘎嘎叫像鸭子。这不是因为有人叫它“duck”。反模式是让普通用户代码进行类型检查,特别是通过使用type()
函数1
在处理内部事务时,2类型检查是必要的,而isinstance()
是首选方法
换句话说,您的代码完全是Pythonic的,它唯一的问题是错误消息(它没有提到slice
s)
披露:我是一名Python核心开发人员
1当绝对需要时,isinstance()
是更好的选择
2我同意像\uuu getitem\uuu
这样的特殊方法,但是getitem的文档明确指出“只允许int和slice类型的对象”