假设我有一个函数将一个参数 iterable 作为输入,我想在迭代上多次迭代。
如果我这样写:
def a_function(an_iterable):
for x in an_iterable:
print(x)
for x in an_iterable:
print(x)
第二个循环可以执行或不执行。
list
,set
,dict
,它们不是迭代器/生成器函数,或range
重新启动自身f=open("file")
获得)。 (重复使用文件迭代器是SO的许多问题的主题)当然,我可以这样做,以避免在不需要时创建不必要的list
:
def a_function(an_iterable):
if any(lambda x : type(an_iterable==x) for x in (range,list,set,dict))):
# use as-is
pass
else:
an_iterable = list(an_iterable)
for x in an_iterable:
print(x)
for x in an_iterable:
print(x)
适用于很多常见情况,但不适用于一般情况。
有没有一种干净的方法来检测我是否可以在我的可迭代对象上多次迭代?
答案 0 :(得分:1)
您可以使用collections.abc.Sequence
类来查看迭代是否实际上是一个序列:
>>> from collections.abc import Sequence
>>> isinstance([1,2,3], Sequence)
True
>>> isinstance((1,2,3), Sequence)
True
>>> isinstance(range(10), Sequence)
True
>>> isinstance(iter((1,2,3)), Sequence)
False
这不适用于套装:
>>> isinstance({1,2,3}, Sequence)
False
如果要包含集和映射,请使用collections.abs.Set
和collections.abc.Mapping
:
>>> isinstance({1,2,3}, (Sequence, Set, Mapping))
True
您可能想要创建一个辅助函数,在需要时将迭代转换为序列:
def sequencify(iterable):
if isinstance(iterable, (Sequence, Set, Mapping)):
return iterable
return list(iterable)
现在你可以这样做:
def a_function(iterable):
iterable = sequencify(iterable)
for x in iterable:
print(x)
for x in iterable:
print(x)
更简单的替代方法是检查iterable
参数不是否有__next__
方法:
>>> hasattr([1,2,3], '__next__')
False
>>> hasattr(iter([1,2,3]), '__next__')
True
这是有效的,因为良好实现的容器只是迭代而不是迭代器本身,因此它们只有一个__iter__
方法返回一个迭代器,该迭代器具有推进迭代的__next__
方法。
这将导致:
def sequencify(iterable):
if not hasattr(iterable, '__next__'):
return iterable
return list(iterable)
最后的选择是最简单的:将参数记录为序列和不 iterable 并让用户负责提供正确的类型。
答案 1 :(得分:0)
您可以检查对象是否具有__next__()
方法(Python 3)或next()
方法(Python 2)。如果确实如此,那么您可能会认为它实现了迭代器协议,因此其值不可重用。
>>> l = [1, 2, 3, 4]
>>> it = iter(l)
>>> hasattr(l, '__next__')
False
>>> hasattr(it, '__next__')
True
如果有__next__
属性,则应检查它是否可调用:
>>> hasattr(l, '__next__') and callable(l.__next__)
False
>>> hasattr(it, '__next__') and callable(it.__next__)
True