我想知道是否还有其他事情发生。创建生成器有一些开销,但我认为将条件if x==device
在生成器中强制它生成整个列表,并在 next() 运行之前创建一个新列表。
请参阅此示例,比较强制创建新列表的列表推导式和惰性且不强制创建新列表的生成器:
>>> from timeit import Timer
>>> # List comprehension forces a new list to be created in memory
>>> def f1():
... q = [x for x in xrange(1000)]
... r = q[1]
... return r
...
>>> # Generator comprehension does 'lazy' iteration, only when needed
>>> def f2():
... q = (x for x in xrange(1000))
... r = next(q)
... return r
...
>>> Timer(f1).timeit()
47.420308774268435
>>> Timer(f2).timeit()
1.346566078497844
看到列表理解要慢得多,并且生成器惰性方法意味着它仅在您调用 next() 时才开始迭代,获取一个值并停止。
现在这个例子,唯一的变化是两者都采用最后一个元素if x = 999
:
>>> # List comprehension still forces creation of a new list
>>> # although the list only ends up with one element
>>> # nb. it's the last element
>>> def f1():
... q = [x for x in xrange(1000) if x == 999]
... r = q[0]
... return r
...
>>> # Generator comprehension is lazy
>>> # nb. it also only returns the last element
>>> def f2():
... q = (x for x in xrange(1000) if x == 999)
... r = next(q)
... return r
...
>>> Timer(f1).timeit()
37.279105355189984
>>> Timer(f2).timeit()
37.46816399778598
看看现在他们基本是一样的了。发电机的速度已经减慢了。该条件迫使它执行与列表理解相同的操作,它不能懒惰地只获取匹配的一件事情而不评估整个列表。
所以我认为在你的例子中,你不是just看到创建生成器然后在其他人回答时调用它的开销,正如我最初的评论所说。
我认为通过包括if x==device
条件,您强制生成器构造迭代整个列表,and创建一个新的列表对象,and用所有结果填充它,then在该新列表上创建一个生成器并then调用它来获取结果。
所以有一个lot比 for 循环迭代现有列表的开销更大,这不是因为next()
本质上是缓慢的。
编辑:当生成器表达式被添加到Python中时,您可以在提案中看到它:PEP-0289 - 生成器表达式 https://www.python.org/dev/peps/pep-0289/,在关于的部分早期绑定与后期绑定 https://www.python.org/dev/peps/pep-0289/#early-binding-versus-late-binding
当被要求总结绑定第一个表达式的推理时,Guido 给出了 [5] :
考虑sum(x for x in foo())
。现在假设 foo() 中有一个错误
会引发异常,并且 sum() 中的错误会引发
在开始迭代其参数之前发生异常。哪个
您希望看到什么异常?如果其中的一个我会感到惊讶
sum() 被提出,而不是 foo() 中的那个,因为对 foo() 的调用
是 sum() 参数的一部分,我希望参数是
在调用函数之前进行处理。
奥托,在sum(bar(x) for x in foo())
,其中 sum() 和 foo()
没有错误,但是 bar() 引发了异常,我们别无选择,只能
延迟对 bar() 的调用,直到 sum() 开始迭代——这就是
发电机合同的一部分。 (他们什么都不做,直到他们
首先调用 next() 方法。)
换句话说,如果x==device
将会抛出异常,因为列表中的一项无法比较,例如来自自定义对象的类型错误,您希望在调用 next() 之前看到该异常,从而强制迭代整个列表,失去您可能希望看到的生成器懒惰的节省,并创建更多列表与 for 循环相比,对象创建开销。