Python 为什么在这种特殊情况下使用生成器对象?
我在看一段从网上下载的代码。这是为一个基本的网络爬虫。我遇到了以下Python 为什么在这种特殊情况下使用生成器对象?,python,list,web-crawler,generator,Python,List,Web Crawler,Generator,我在看一段从网上下载的代码。这是为一个基本的网络爬虫。我遇到了以下for循环: for link in (links.pop(0) for _ in xrange(len(links))): ... 现在,我觉得以下代码也可以工作: for link in links: .... links=[] 通过研究,我发现第一个实例清除了链接,还生成了一个生成器对象(genexpr)链接从未在for循环中使用,因此其长度的减少与代码无关 使用xrange并每次弹出元素有什么特别的原因
for
循环:
for link in (links.pop(0) for _ in xrange(len(links))):
...
现在,我觉得以下代码也可以工作:
for link in links:
....
links=[]
通过研究,我发现第一个实例清除了链接
,还生成了一个生成器对象(genexpr)
<代码>链接从未在for
循环中使用,因此其长度的减少与代码无关
使用xrange并每次弹出元素有什么特别的原因吗?也就是说,使用generator对象是否比调用标准列表中的元素更有优势?此外,在什么情况下发电机是有用的;为什么?很难为您引用的代码找到任何理由 我能想到的唯一一件事是
链接中的对象可能很大,或者与稀缺资源相关,因此尽快释放它们(而不是等到循环结束后才释放它们)可能很重要。但是(a)如果是这样,最好在创建每个链接时对其进行处理(可能使用生成器来组织代码),而不是在开始处理之前构建整个链接列表;(b)即使您别无选择,只能在处理之前构建整个列表,清除每个列表条目也比弹出列表便宜:
for i, link in enumerate(links):
links[i] = None
...
(从包含n个项目的列表中弹出第一个元素需要O(n),尽管在实践中它会相当快,因为它是使用memmove
实现的)
即使您绝对坚持在迭代时反复弹出列表,也最好这样编写循环:
while links:
link = links.pop(0)
...
生成器的目的是避免生成大量的中间对象集合,这些中间对象不会为任何外部用途服务
如果所有代码都是在页面上构建链接集,那么第二个代码段就可以了。但可能需要的是一组根网站名称(例如google.com而不是google.com/q=some_search_term…)。如果是这样的话,那么你应该先查看链接列表,然后浏览完整的列表,去掉第一部分
这是第二个剥离部分,你可以通过使用发电机获得更多。你现在可以一个接一个地浏览每个链接,获得网站名称,而不需要一个包含所有链接的大中间列表。也许for
循环(在你的问题中省略)中的代码取决于len(链接)这一事实
每次迭代都会减少?@FrédéricHamidi len(links)只会被调用once@robert,我指的是循环中的代码,我们看不到,而不是调用xrange()
@FrédéricHamidi ah抱歉,你是对的。正如我提到的,链接
从未在循环中使用。甚至它的长度都没有。它更短,更清晰,因为操作更少,并且避免了构造不必要的xrange
和生成器对象。但是每个人都有自己的喜好,所以使用生成器版本不会错。在类似的情况下,我甚至使用了一个ListPart
案例,它支持迭代和索引,以从列表中删除部分。如果评论允许换行符,我会把它放在这里(所以不可能这样做可能是好事);它可以像a=ListPart([1,2,3,4])那样使用;b=a[2:4];c=清单(a);打印a、b、c
并打印myiter.ListPart([])[3、4][1、2]
。但即使在第二种情况下,假设我们正在剥离链接,我们最终一次只处理一个链接。for
循环中的所有操作都处理链接
,而不是链接
。Python不会生成新的列表,无论link
更改了多少。@shashwat是的,但是您创建的中间对象越少,性能就越好。速度也将通过生成生成器并在最后一刻调用生成器上的list()返回。一个列表中的很多附件会比较慢。@shashwat我不认为这样使用xrange(len(…)有什么意义