为什么发电机更快?

问题描述 投票:0回答:2

我知道生成器比迭代器更快。我还了解生成器可以使用

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”循环是使用迭代器实现的。如果你对这些进行计时,生成器就会始终更快。当生成器使用迭代器时,为什么会这样?

谢谢。

python iterator generator
2个回答
11
投票

在您的代码中,

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

生成器版本速度较慢,这是常见的情况。


0
投票

在您的代码中,您定义了两个函数:

myGen
myIter
,并且您想要比较它们的性能。
myGen
是生成器函数,
myIter
是常规循环。我们来分析一下这两个函数的性能。

  1. myGen
    是一个生成器函数,它在循环中产生一个值
    x
    。但是,您尚未在代码中定义
    x
    是什么。我认为这是您提供的代码中未显示的变量。

  2. 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))
来消耗生成器,以便正确测量其性能。

一般来说,对于像您所展示的那样的简单操作,常规循环比生成器更快。当您拥有大型数据集或想要执行惰性评估时,生成器会更有用。

© www.soinside.com 2019 - 2024. All rights reserved.