Python 3.6.8 (default, Oct 7 2019, 12:59:55)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.9.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: def yield_from_generator():
...: yield from (i for i in range(10000))
...:
In [2]: def yield_from_list():
...: yield from [i for i in range(10000)]
...:
In [3]: import timeit
In [4]: timeit.timeit(lambda: list(yield_from_generator()), number=10000)
Out[4]: 5.3820097140014695
In [5]: timeit.timeit(lambda: list(yield_from_list()), number=10000)
Out[5]: 4.333915593000711
我运行yield from
生成器和yield from
多次列出。列表版本始终提供更好的性能,而我的直觉告诉我相反的结论-创建列表需要在启动时分配内存。为什么我们会注意到这种性能差异?
简短的答案是,表面语法使它们看起来比实际看起来更相似
我将更详细地分解一系列功能(dis
模块对此很有帮助),我将把这些东西分为设置成本和每产值成本。我们开始于:
def yield_from_generator():
yield from (i for i in range(10000))
费用是:
genexpr
的收益,它还会在next
迭代器上调用range
。请注意,这里有两个上下文开关接下来我们看:
def yield_from_list():
yield from [i for i in range(10000)]
费用为:
list
操作码,因此会很快list
的迭代器就可以了,所以很快接下来我们看一个类似的功能:
def yield_from_list2():
yield from list(i for i in range(10000))
这不使用特殊的列表操作码,并且具有生成器的双重嵌套,因此再次变慢。费用是:
list
的迭代器,所以又快了最后是一个只强调yield from
的快速版本:
def yield_from_generator2():
yield from range(10000)
费用为:
range
对象range
迭代器我的笔记本电脑上所有这些的时间是:
yield_from_generator 639 µs
yield_from_list 536 µs
yield_from_list2 689 µs
yield_from_generator2 354 µs
希望现在更加清晰了。另一个版本是:
def yield_from_list3():
yield from list(range(10000))
以401 µs的速度运行,但希望它在性能上居于中间,这是显而易见的