4

我正在查看从互联网上下载的一些代码。这是一个基本的网络爬虫。我遇到了以下for循环:

for link in (links.pop(0) for _ in xrange(len(links))):
    ...

现在,我觉得下面的代码也可以工作:

for link in links:
    ....
links=[]

研究,我发现第一个实例清除links并生成一个generator object (genexpr). links从未在for循环中使用,因此它的递减长度与代码无关。

每次使用 xrange 并弹出元素有什么特别的原因吗?即使用生成器对象比调用标准列表的元素有什么优势吗?此外,在什么情况下生成器会有用;为什么?

4

2 回答 2

6

很难看到您引用的代码有任何理由。

我唯一能想到的是其中的对象links可能很大,或者与稀缺资源相关联,因此尽快释放它们可能很重要(而不是等到循环结束才释放它们) )。但是 (a) 如果是这样,最好在创建每个链接时处理它(可能使用生成器来组织代码),而不是在开始处理之前建立整个链接列表;(b) 即使您别无选择,只能在处理之前建立整个列表,清除每个列表条目比弹出列表更便宜:

for i, link in enumerate(links):
    links[i] = None
    ...

(从具有n 个项目的列表中弹出第一个元素需要 O( n ),尽管实际上它会相当快,因为​​它是使用 实现的memmove。)

即使您绝对坚持在遍历列表时反复弹出列表,最好像这样编写循环:

while links:
    link = links.pop(0)
    ...
于 2012-12-06T12:29:42.840 回答
0

生成器的目的是避免构建大量不为任何外部使用提供服务的中间对象。

如果所有代码都是在页面上构建一组链接,那么第二个代码片段就可以了。但也许可能需要的是一组根网站名称(例如 google.com 而不是 google.com/q=some_search_term....)。如果是这种情况,您将获取链接列表,然后浏览完整列表,仅删除第一部分。

这是第二个剥离部分,您可以通过使用发电机获得更多收益。与其不必要地构建一个需要内存和时间来构建的链接列表,您现在可以一个接一个地通过每个链接,在没有所有链接的大中间列表的情况下获得网站名称。

于 2012-12-06T12:35:25.387 回答