是否有一种统一的方法来知道迭代是否会消耗一个可迭代对象?
假设您有一个函数crunch,它为参数请求一个可迭代的对象,并多次使用它。类似于:
def crunch (vals):
for v in vals:
chomp(v)
for v in vals:
yum(v)(注意:将两个for循环合并在一起不是一个选项)。
如果函数使用可迭代(不是列表)来调用,则会出现问题。在下面的调用中,永远不会执行yum函数:
crunch(iter(range(4))我们原则上可以通过重新定义crunch函数来解决这个问题,如下所示:
def crunch (vals):
vals = list(vals)
for v in vals:
chomp(v)
for v in vals:
yum(v)但是,如果对crunch的调用是:
hugeList = list(longDataStream)
crunch(hugeList)我们可以通过这样定义crunch来解决这个问题:
def crunch (vals):
if type(vals) is not list:
vals = list(vals)
for v in vals:
chomp(v)
for v in vals:
yum(v)但是仍然存在这样的情况:调用代码将数据存储在
例如:
from collections import deque
hugeDeque = deque(longDataStream)
crunch(hugeDeque)最好有一个isconsumable谓词,这样我们就可以像这样定义crunch:
def crunch (vals):
if isconsumable(vals):
vals = list(vals)
for v in vals:
chomp(v)
for v in vals:
yum(v)这个问题有解决办法吗?
发布于 2013-03-13 08:27:53
另一个额外的选项可以是查询迭代器是否是它自己的迭代器:
if iter(vals) is vals:
vals = list(vals)因为在这种情况下,它只是一个迭代器。
这适用于生成器、迭代器、文件和许多其他对象,这些对象都是为“一次运行”而设计的,换句话说,所有迭代器本身都是迭代器,因为是__iter__()。
但这可能还不够,因为有一些对象在迭代过程中清空自己,而不是自己的迭代器。
通常,自用对象将是自己的迭代器,但在某些情况下可能不允许这样做。
假设有一个类包装一个列表,并在迭代时清空这个列表,例如
class ListPart(object):
"""Liste stückweise zerlegen."""
def __init__(self, data=None):
if data is None: data = []
self.data = data
def next(self):
try:
return self.data.pop(0)
except IndexError:
raise StopIteration
def __iter__(self):
return self
def __len__(self): # doesn't work with __getattr__...
return len(self.data)你把它叫做
l = [1, 2, 3, 4]
lp = ListPart(l)
for i in lp: process(i)
# now l is empty.如果我现在将额外的数据添加到该列表中,并再次遍历同一个对象,我将得到新的数据,即违反议定书
该协议的意图是,一旦迭代器的
next()方法引发StopIteration,它将在随后的调用中继续这样做。不遵守此属性的实现被视为已破坏。(这个约束是在Python2.3中添加的;在Python2.2中,各种迭代器根据这个规则被打破了。)
因此,在这种情况下,对象将不得不返回一个与其本身不同的迭代器,尽管它是自我消耗的。在这种情况下,这可以通过
def __iter__(self):
while True:
try:
yield l.pop(0)
except IndexError: # pop from empty list
return它会在每次迭代中返回一个新生成器--在我们讨论的情况下,这将通过mash来实现。
发布于 2013-03-13 08:21:37
一种可能是使用isinstance(val, collections.Sequence)测试项目是否是序列。不消耗仍然没有完全保证,但我认为这是你能得到的最好的。Python序列必须有一个长度,这意味着它至少不能是一个开放的迭代器,而且通常意味着元素必须提前被知道,这反过来意味着它们可以在不消耗它们的情况下被迭代。仍然可以编写符合序列协议但不可重传的病理类,但您将永远无法处理这些问题。
请注意,Iterable和Iterator都不是合适的选择,因为这些类型不能保证长度,因此不能保证迭代甚至是有限的,更不用说可重复了。但是,您可以同时检查Sized和Iterable。
重要的是记录您的函数将对其参数进行两次迭代,从而警告用户他们必须传入一个支持该参数的对象。
发布于 2013-03-13 08:21:24
def crunch (vals):
vals1, vals2 = itertools.tee(vals, 2)
for v in vals1:
chomp(v)
for v in vals2:
yum(v)在这种情况下,tee将在内部存储vals的完整性,因为一个迭代器是在另一个迭代器启动之前完成的。
https://stackoverflow.com/questions/15379994
复制相似问题