我想要一个函数
isiterable(t: type) -> bool
,如果我传递的类型是可迭代的,它将返回 true,否则返回 false。
assert isiterable(int) == False
assert isiterable(str) == False
assert isiterable(Iterable[int]) == True
我使用类型提示来装饰函数已经有一段时间了。但是,我现在正在编写一些需要类型提示感知的代码。我对此知之甚少。我的目标是 python 3.7,如果这有什么区别的话。所以这个问题可能有一个明显的答案。
我可以通过蛮力来构造它 - 检查类型类的名称是否为
_GenericAlias
,然后检查 _name
是否为 Iterator
- 但由于多种原因,这感觉不稳健或面向未来!
编辑:我的问题似乎写得不清楚。我想我所追求的叫做类型内省。我在下面得到了一个可行的答案,但几乎可以肯定至少不是Pythonic。我正在尝试支持 3.7 和 3.8,如果有什么区别的话 - 不过支持 3.6 会很好。
检查文档https://docs.python.org/3/library/collections.abc.html?highlight=iterable#collections.abc.Iterable
如果你写了类似的内容:
from collections.abc import Iterable
def isiterable(obj):
return isinstance(obj, Iterable)
它只会检查它是否是
Iterable
的子类或者是否具有 __iter__
方法。这不准确。
任何具有
__iter__
或 __getitem__
的对象都是可迭代的。所以你可以使用这两种方法来编写一个Iterable
# with __iter__ and __next__
class Foo:
def __init__(self, x):
self.x = x
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n <= self.x:
self.n += 1
return 1
else:
raise StopIteration
# with __setitem__ and __getitem__
class Bar(object):
def __init__(self, x):
self.x = [None] * x
def __setitem__(self, index, data):
self.x[index] = data
def __getitem__(self, index):
return self.x[index]
判断对象是否可迭代的唯一可靠方法是调用iter(obj)
def isiterable(obj):
try:
print(iter(obj))
except:
return False
else:
return True
print(isiterable(Foo(1)))
print(isiterable(Bar(1)))
# <__main__.Foo object at 0x7f3fe2703bb0>
# True
# iterator object at 0x7f3fe269f400>
# True
这是可行的 - 但感觉它取决于实现细节而不是类型系统的公共 API。我暂时将此标记为答案,直到我或其他人发布更正确的内容:
from typing import Type
def is_iterable(t: Type) -> bool:
if type(i_type).__name__ != '_GenericAlias':
return False
import collections
if i_type.__origin__ != collections.abc.Iterable:
return False
return True
如果您还想知道
X
中的 Iterable[X]
是什么,您可以通过 i_type.__args__[0]
获取。
import typing
from collections import abc
def is_iterable(type_hint: type) -> bool:
return (hasattr(type_hint, '__origin__')
and type_hint.__origin__ in {abc.Iterable, typing.Iterable})