我知道生成器比迭代器更快。我还了解生成器可以使用
for
循环语法来实现。例如:
import time
startT = time.time()
def myGen(n):
for i in range(n):
yield x
def myIter(n):
for i in range(n):
pass
def main():
n=100
startT=time.time()
myIter(n)
print 'myIter took ', time.time() - startT
startT=time.time()
myGen(n)
print 'myGen(n) took ', time.time() - startT
这只是结果的一个示例:
myIter took 0.09234782
myGen(n) took 0.017847266
由于这使用了
for
循环语法,那么我不明白它如何比迭代器更快。该生成器使用迭代器,因为“for”循环是使用迭代器实现的。如果你对这些进行计时,生成器就会始终更快。当生成器使用迭代器时,为什么会这样?
谢谢。
在您的代码中,
myIter(n)
确实有效——它循环了 100 次。
另一方面,myGen(n)
,只需构建生成器——仅此而已。它数不到 100。您所做的只是计算构建对象所需的时间,并且您以不可靠的方式对其进行计时。如果我们使用 timeit
(这里使用 IPython 让事情变得更简单):
>>> %timeit myIter(100)
1000000 loops, best of 3: 1 µs per loop
>>> %timeit myGen(100)
10000000 loops, best of 3: 163 ns per loop
>>> %timeit myGen(10**1000)
10000000 loops, best of 3: 163 ns per loop
我们看到
myGen(n)
时间与 n
无关,因为它没有做任何事情。事实上,我们可以看到你的代码从未以其他方式执行过:
>>> list(myGen(100))
Traceback (most recent call last):
File "<ipython-input-11-dd43d937402a>", line 1, in <module>
list(myGen(100))
File "<ipython-input-1-ba968e48e9fd>", line 3, in myGen
yield x
NameError: name 'x' is not defined
如果我们修复这个拼写错误,然后尝试一种快速的方法来消耗生成器,我们会得到类似的东西
>>> %timeit myIter(100)
1000000 loops, best of 3: 1 µs per loop
>>> %timeit consume(myGen(100), 100)
100000 loops, best of 3: 3.44 µs per loop
生成器版本速度较慢,这是常见的情况。
在您的代码中,您定义了两个函数:
myGen
和myIter
,并且您想要比较它们的性能。 myGen
是生成器函数,myIter
是常规循环。我们来分析一下这两个函数的性能。
myGen
是一个生成器函数,它在循环中产生一个值 x
。但是,您尚未在代码中定义 x
是什么。我认为这是您提供的代码中未显示的变量。
myIter
是一个常规循环,迭代 n
次,但在循环内不执行任何特定操作。
性能对比方面:
myIter
通常会更快,因为它只是一个简单的循环,没有任何创建和管理生成器对象的额外开销。
myGen
涉及创建生成器对象并产生一个值,这需要一些额外的开销。当您需要延迟计算或希望通过不一次生成所有值来节省内存时,生成器非常有用。然而,在这种特定情况下,生成器并没有比简单循环提供任何优势。
这是经过一些更正的修改后的代码:
import time
def myGen(n, x):
for i in range(n):
yield x
def myIter(n):
for i in range(n):
pass
def main():
n = 100
x = 42 # Define the value to yield in myGen
startT = time.time()
myIter(n)
print('myIter took', time.time() - startT)
startT = time.time()
list(myGen(n, x)) # Consume the generator with a list to measure its performance
print('myGen(n) took', time.time() - startT)
if __name__ == "__main__":
main()
在此代码中,我们定义了值
x
,该值被传递给 myGen
。我们还使用 list(myGen(n, x))
来消耗生成器,以便正确测量其性能。
一般来说,对于像您所展示的那样的简单操作,常规循环比生成器更快。当您拥有大型数据集或想要执行惰性评估时,生成器会更有用。