哪个通常更快,产量还是追加?

时间:2010-08-15 14:34:33

标签: python performance return generator yield

我目前正处于个人学习项目中,我在XML数据库中阅读。我发现自己正在编写收集数据的函数,而我不确定返回它们的快捷方法。

通常更快:

  1. yield s或
  2. 函数中的几个append()然后return随后的list
  3. 我很高兴知道yield s会比append()更快,反之亦然。

4 个答案:

答案 0 :(得分:16)

yield具有 lazy 的巨大优势,速度通常不是最佳使用它的原因。但如果它在你的环境中有效,那么没有理由不使用它:

# yield_vs_append.py
data = range(1000)

def yielding():
    def yielder():
        for d in data:
            yield d
    return list(yielder())

def appending():
    lst = []
    for d in data:
        lst.append(d)
    return lst

结果如下:

python2.7 -m timeit -s "from yield_vs_append import yielding,appending" "yielding()"
10000 loops, best of 3: 80.1 usec per loop

python2.7 -m timeit -s "from yield_vs_append import yielding,appending" "appending()"
10000 loops, best of 3: 130 usec per loop

至少在这个非常简单的测试中,yield比追加更快。

答案 1 :(得分:8)

我最近问自己一个类似的问题,探索通过附加到列表或通过生成器生成列表(或元组)的所有排列的方法,并找到(对于长度为9的排列,大约需要一秒钟左右)生成):

  • 天真的方法(排列是列表,附加到列表,返回列表列表)大约是itertools.permutations
  • 的三倍
  • 使用生成器(即yield)可以减少约1秒。 20%
  • 使用生成器并生成元组是最快的,大约是itertools.permutations的两倍。

带上一粒盐!时间和分析非常有用:

if __name__ == '__main__':
    import cProfile
    cProfile.run("main()")

答案 2 :(得分:6)

有更快的替代TH4Ck的屈服()。这是列表理解。

In [245]: def list_comp():
   .....:     return [d for d in data]
   .....:

In [246]: timeit yielding()
10000 loops, best of 3: 89 us per loop

In [247]: timeit list_comp()
10000 loops, best of 3: 63.4 us per loop

当然,在不知道代码结构的情况下对这些操作进行微观基准测试是相当愚蠢的。他们每个人在差异情况下都很有用。例如,如果要应用可以表达为单个表达式的简单操作,则列表理解很有用。 Yield有一个显着的优势,您可以将遍历代码隔离到生成器方法中。哪一个适当取决于使用情况。

答案 3 :(得分:0)

首先你必须决定,如果你需要发电机,这也有改进的方法。像列表生成器“[elem for elem in somethink]”。如果你只是在列表中使用值来进行某些操作,建议使用生成器。但是如果你需要列表进行许多更改,并且同时使用许多元素,那么这必须是列表。 (比如标准程序员使用列表的70%,更好的是生成器。使用更少的内存,只是很多人只是没有看到列表的其他方式。不幸的是在我们的时代,许多人在良好的optymalization撒尿,并只是为了工作。)

如果你使用生成器列表来提高回报率,那么让我们对yield人员这样做。无论如何,我们为Python编程语言中的所有操作提供了多个更优化的方法。

收益率快于回报,我会证明这一点。 检查一下这个家伙:

data = range(1000)

def yielder():
    yield from data

def appending():
    L = []
    app = list.append
    for i in data:
        app(L, i)
    return L

def list_gen():
    return [i for i in data]

当然,追加将比其他想法慢,因为我们创建并扩展列表的任何循环时间。只是循环“for”是非常不合理的,如果你可以避免这种情况,那就去做吧。在任何一步,这个函数加载下一个元素并编写我们的变量,在内存中得到这个对象值。所以我们跳转到任何元素,创建引用,在循环中扩展列表(声明的方法是巨大的速度optymalizer),当我们生成只返回时,summary在两个列表中获得2000个元素。

list_gen在内存方面较少,我们只返回元素,但是像向上一样,我们生成了secound列表。现在我们得到了两个列表,原始数据和她的副本。 2000年要素摘要。只是我们避免使用创建引用变量的步骤。在列表中包含我们的gen避免这一步骤。只需写出元素。

yielder使用最少的内存,因为我们只是从数据中获得了价值。我们避免一个参考。例如:

data = range(1000)

def yielder():
    yield from data

def list_gen():
    return [i for i in data]

#Now we generate next reference after line [i for i in data]
for i in list_gen():
    #some instruction

#This is our first reference, becouse was yield from data.
for i in yielder():
    #some instruction

对一些指令只使用一个元素,而不是列表中的所有指令,下一个值yielder将在下一个循环返回,而不是杂志所有1000个元素在参考中写入。

对于小小的挖掘主题,当我不小心从谷歌搜索中传出一个十字架时,其他初学python程序员可以看到这个废话。