python - `yield from` 生成器与 `yield from` 列表性能

标签 python list generator yield coroutine

这个问题在这里已经有了答案:





Why converting list to set is faster than converting generator to set?

(1 个回答)



List comprehension vs generator expression's weird timeit results?

(3 个回答)


2年前关闭。




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 的产量,它也调用 nextrange迭代器。注意这里有两个上下文切换

  • 接下来我们看:
    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))
    

    这不使用特殊列表操作码并且具有生成器的双重嵌套,因此再次变慢。费用是:
  • setup:创建一个新的生成器表达式并将其传递给列表构造函数,这将遍历遍历范围对象
  • 的生成器表达式
  • 每产量:使用 list的迭代器所以又快了

  • 最后是一个快速版本,只是强调 yield from :
    def yield_from_generator2():
        yield from range(10000)
    

    费用是:
  • 设置:创建 range对象
  • 每 yield :简历 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,但希望更明显的是为什么它位于中间,性能方面

    关于python - `yield from` 生成器与 `yield from` 列表性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60150962/

    相关文章:

    python - 过滤不静音视频ffmpeg-python

    python - 需要让我的 python web 应用程序像守护进程一样 self 监控(保持事件状态)

    python - Numpy 数组的快速插值/重采样 - Python

    python - 如何遍历列表列表的列表列表...... python 中的一对坐标列表?

    python - 在 Python 3.x 的整数列表中限制用户输入

    class - 更改符合 'generate()' 的类的子类的 'SequenceType' 方法

    python - 为什么空字典的大小与 Python 中非空字典的大小相同?

    python - 在 Python 中比较列表和省略号

    python - 在 python 中调用生成器时引发 Unresolved 引用错误

    Python——生成器在迭代器之间散布值——我这样做正确吗?