什么时候不是使用python生成器的好时机?

时间:2008-10-29 04:25:03

标签: python optimization iterator generator

这是What can you use Python generator functions for?的反过来:python生成器,生成器表达式,itertools模块是我最近最喜欢的python特性。它们在设置操作链以对大量数据执行时特别有用 - 我经常在处理DSV文件时使用它们。

那么什么时候是使用生成器,生成器表达式或itertools函数的好时机?

  • 我应该何时更喜欢zip()而不是itertools.izip()
  • range()超过xrange()
  • [x for x in foo]超过(x for x in foo)

显然,我们最终需要将生成器“解析”为实际数据,通常是通过创建列表或使用非生成器循环对其进行迭代。有时我们只需知道长度。这不是我要问的。

我们使用生成器,因此我们不会将新列表分配到内存中以用于临时数据。这对于大型数据集尤其有用。对于小型数据集也有意义吗?是否有明显的内存/ CPU权衡?

鉴于对list comprehension performance vs. map() and filter()的开放性讨论,我特别感兴趣的是,如果有人对此做了一些分析。 (alt link

10 个答案:

答案 0 :(得分:49)

在以下情况下使用列表而不是生成器:

1)您需要访问数据多次次(即缓存结果而不是重新计算结果):

for i in outer:           # used once, okay to be a generator or return a list
    for j in inner:       # used multiple times, reusing a list is better
         ...

2)您需要随机访问(或除前向顺序订单以外的任何访问权限):

for i in reversed(data): ...     # generators aren't reversible

s[i], s[j] = s[j], s[i]          # generators aren't indexable

3)你需要加入字符串(这需要两遍数据):

s = ''.join(data)                # lists are faster than generators in this use case

4)您正在使用 PyPy ,有时无法通过正常的函数调用和列表操作来优化生成器代码。

答案 1 :(得分:39)

通常,在需要列表操作时不要使用生成器,例如len(),reversed()等。

有时您可能不希望进行延迟评估(例如,事先进行所有计算以便释放资源)。在这种情况下,列表表达式可能会更好。

答案 2 :(得分:24)

个人资料,个人资料,个人资料。

分析您的代码是了解您所做的事情是否有任何影响的唯一方法。

xrange,generator等的大多数用法都是静态大小,小数据集。只有当你到达大型数据集时才能真正发挥作用。 range()vs. xrange()主要是让代码看起来更丑陋,不会丢失任何东西,也许会获得一些东西。

个人资料,个人资料,个人资料。

答案 3 :(得分:17)

您永远不应该zip优先于iziprange超过xrange,或列出对生成器理解的理解。在Python 3.0中,range具有xrange - 类似于语义而zip具有izip - 就像语义一样。

对于那些需要实际列表的时间,列表推导实际上更清晰,如list(frob(x) for x in foo)

答案 4 :(得分:6)

正如您所提到的,“这对大型数据集特别有意义”,我认为这可以回答您的问题。

如果您没有按性能划分任何墙壁,您仍然可以坚持使用列表和标准功能。然后当遇到性能问题时进行切换。

正如评论中@ u0b34a0f6ae所述,在开始时使用生成器可以使您更容易扩展到更大的数据集。

答案 5 :(得分:6)

关于性能:如果使用psyco,列表可以比生成器快得多。在下面的示例中,使用psyco.full()

时,列表的速度提高了近50%
import psyco
import time
import cStringIO

def time_func(func):
    """The amount of time it requires func to run"""
    start = time.clock()
    func()
    return time.clock() - start

def fizzbuzz(num):
    """That algorithm we all know and love"""
    if not num % 3 and not num % 5:
        return "%d fizz buzz" % num
    elif not num % 3:
        return "%d fizz" % num
    elif not num % 5:
        return "%d buzz" % num
    return None

def with_list(num):
    """Try getting fizzbuzz with a list comprehension and range"""
    out = cStringIO.StringIO()
    for fibby in [fizzbuzz(x) for x in range(1, num) if fizzbuzz(x)]:
        print >> out, fibby
    return out.getvalue()

def with_genx(num):
    """Try getting fizzbuzz with generator expression and xrange"""
    out = cStringIO.StringIO()
    for fibby in (fizzbuzz(x) for x in xrange(1, num) if fizzbuzz(x)):
        print >> out, fibby
    return out.getvalue()

def main():
    """
    Test speed of generator expressions versus list comprehensions,
    with and without psyco.
    """

    #our variables
    nums = [10000, 100000]
    funcs = [with_list, with_genx]

    #  try without psyco 1st
    print "without psyco"
    for num in nums:
        print "  number:", num
        for func in funcs:
            print func.__name__, time_func(lambda : func(num)), "seconds"
        print

    #  now with psyco
    print "with psyco"
    psyco.full()
    for num in nums:
        print "  number:", num
        for func in funcs:
            print func.__name__, time_func(lambda : func(num)), "seconds"
        print

if __name__ == "__main__":
    main()

结果:

without psyco
  number: 10000
with_list 0.0519102208309 seconds
with_genx 0.0535933367509 seconds

  number: 100000
with_list 0.542204280744 seconds
with_genx 0.557837353115 seconds

with psyco
  number: 10000
with_list 0.0286369007033 seconds
with_genx 0.0513424889137 seconds

  number: 100000
with_list 0.335414877839 seconds
with_genx 0.580363490491 seconds

答案 6 :(得分:3)

就性能而言,我无法想到您希望在生成器上使用列表。

答案 7 :(得分:3)

我从来没有发现发电机会妨碍你想要做的事情。但是,有很多情况下使用生成器对你的帮助不会超过使用它们。

例如:

sorted(xrange(5))

不提供任何改进:

sorted(range(5))

答案 8 :(得分:3)

如果您需要稍后保留其他值并且您的集合的大小不是太大,您应该更喜欢列表推导。

例如:  您正在创建一个列表,您将在程序中稍后循环几次。

在某种程度上,您可以将生成器视为迭代(循环)与列表推导的替代,作为一种数据结构初始化。如果要保留数据结构,请使用列表推导。

答案 9 :(得分:0)

生成器构建可枚举的值列表。当迭代过程可以按需使用值时,可枚举非常有用。构建你的生成器需要时间,所以如果列表有几百万条记录,那么使用sql server处理sql中的数据可能更有用。