“yield item”与“return iter(items)”相比有哪些优势?

6
在下面的示例中,resp.results是一个迭代器。
版本1:
items = []
for result in resp.results:
     item = process(result)
     items.append(item)
return iter(items)

版本2:

for result in resp.results:
     yield process(result)

在性能和内存节省方面,版本1中返回iter(items)是否比仅返回items更好/更差?

在“Python Cookbook”中,Alex表示明确使用iter()“更灵活但使用频率较低”,但是返回iter(items)与版本2中的yield相比有哪些优缺点呢?

此外,检查迭代器和/或yield的最佳方法是什么?--您不能使用len(results)来检查列表的大小吗?


3
版本2更短,而且不创建中间列表对象。为什么你不直接每次都使用它呢?为什么你认为这些方法是“不明确”或“等效的”?仅返回一个迭代器并不是真正的问题所在,是吗? - S.Lott
1
这里有一个很好的yield关键字解释:https://dev59.com/yXVC5IYBdhLWcg3woSpW - Brandon Moretz
1
那么,什么时候会使用iter() -- 根据(https://dev59.com/yXVC5IYBdhLWcg3woSpW#237028) -- 'for'循环不是自动对所有列表执行'iter(mylist)'吗? - espeed
@James,为什么不创建另一个生成器?我认为这并不昂贵。 - OnesimusUnbound
@S.Lott 虽然从技术上讲这是正确的,但你可以使用itertools中的tee将一个迭代器分成两个副本。 - wheaties
显示剩余2条评论
5个回答

4
第一个会计算并存储所有结果,而第二个是一种延迟加载,只有在请求时才会计算结果。也就是说,一个将存储和创建N个项目的列表,而另一个将存储和创建0个项目,直到您开始迭代它们。
更好的思考方式是使用ifilter(来自itertools),其中您正在执行与yield相同的操作,只不过您正在生成迭代器而不是生成器:
 ifilter(process, resp.results)

我发现在2.x系列中,迭代器的执行速度通常比生成器更快,但我无法验证在3.x系列中是否存在任何成本节省。


4

如果需要,将迭代器或生成器转换为列表非常容易:

results = [item for item in iterator]

还有一种更简单的方法,如评论中所指出:

results = list(iterator)

4
为什么不直接使用list(iterator)呢? - isakkarlsson
谢谢。这将有助于单元测试迭代器 - 有关yield语句的单元测试技巧吗? - espeed
@isakkarlsson,为什么不呢?Python的哲学之一是“有且只有一种明显的方法”来做任何事情,但我总是被例外情况绊倒。 - Mark Ransom
@James,迭代器或生成器也是一样的。事实上,它们非常相似,以至于我经常混淆它们。 - Mark Ransom

3

2

您可以创建无限迭代器,但不能创建无限列表:

def fibGen():
    f0, f1 = 0, 1
    while True:
        yield f0
        f0, f1 = f1, f0+f1

1

前面代码片段的优点和缺点是所有结果都会提前计算。如果检索每个项目之间的时间很关键,则这很有用,但如果可迭代对象是无限的或空间是一个问题,则不适用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接