问题描述
这是关于python(动态)类型系统如何工作的问题。我在网上阅读过一些文章,其中说要定义一个“可迭代”的类,我们需要为其定义一个__iter__
函数。实际上,我们不必明确声明该类“是可迭代的”。根据其他语言的经验,我会猜到我不得不写类似的东西
class Foo extends Iterable:
def __iter__(self):
return self
当我测试Foo
的类型及其实例之一时,我得到:
print(type(Foo))
print(type(Foo()))
print(isinstance(Foo(),collections.abc.Iterable))
输出:
<class 'type'>
<class '__main__.Foo'>
True
我的问题是:python(动态)类型系统中类似“ Iterable”的概念的状态如何?我是否应该将其与类型完全相关?
解决方法
type
是python调用通过class
语句定义的任何对象的对象。使用Java作为参考点,type
与代表类的类java.lang.Class
类似。
“ Iterable”更类似于 interface 而不是 class -虽然类/类型定义了内部状态和某些方法,但是接口仅定义了那些方法。 Python并不像其他语言那样将其形式化,但是该原理在python的大多数“隐藏”方法(在两侧都带有两个下划线的方法)中都使用了。如果为类定义了特定的隐藏方法(例如__iter__()
),则该类被认为是可迭代的*。
在您的示例中,您使用isinstance()
来证明您的观点。 The python documentation actually has a page on collections.abc
,其中详细介绍了他们的行为:
此模块提供抽象基类,可用于测试类是否提供特定接口;例如,它是否可散列还是映射。
(为强调而加粗)。
它甚至专门提到了迭代器:
class collections.abc.Iterable
提供
__iter__()
方法的类的ABC。检查
isinstance(obj,Iterable)
会检测已注册为Iterable或具有__iter__()
方法的类,但不会检测到使用__getitem__()
方法进行迭代的类。确定对象是否可迭代的唯一可靠方法是调用iter(obj)
。
Python允许ABC类从本质上劫持了内置的isinstance()
调用via defining the __instancecheck__()
metaclass method,这就是为什么isinstance(Foo(),Iterable)
可以返回True的原因,尽管Foo
不继承自{{1} }。
*虽然这些功能的输入和输出没有像Java这样的静态类型的语言严格定义,但调用它们的内置方法却具有非常具体的期望 ,结果相同。例如,我曾经遇到一个问题,试图覆盖对象上的Iterable
以返回浮点数而不是整数,因为内置__len__()
试图在该对象上使用时抛出错误对象。