什么时候不适合使用python生成器?
这是相反的什么你可以使用Python生成器函数? :python生成器,生成器expression式和itertools
模块是我现在最喜欢的python特性之一。 当设置一系列操作来执行一大堆数据时,它们特别有用 – 我经常在处理DSV文件时使用它们。
那么,什么时候使用一个生成器,一个生成器expression式或者一个itertools
函数呢?
- 什么时候应该比较喜欢
zip()
itertools.izip()
或者 -
range()
通过xrange()
,或 -
[x for x in foo]
over(x for x in foo)
?
显然,我们最终需要将一个生成器“parsing”为实际的数据,通常是通过创build一个列表或用一个非生成器循环对其进行迭代。 有时我们只需要知道长度。 这不是我要问的。
我们使用生成器,因此我们不会将新列表分配给内存中的临时数据。 这对大数据集尤其有意义。 对于小数据集也有意义吗? 有一个明显的内存/ CPU的权衡?
我特别感兴趣的是,如果有人对此进行了一些分析,那么根据对列表理解性能与map()和filter()的令人eye目的讨论。 ( alt链接 )
在以下情况下使用列表而不是生成器:
1)您需要多次访问数据(即caching结果而不是重新计算):
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)你需要连接string(这需要两遍数据):
s = ''.join(data) # lists are faster than generators in this use case
4)你正在使用PyPy ,它有时不能像正常的函数调用和列表操作那样尽可能地优化生成器代码。
一般来说,当你需要列表操作时,不要使用生成器,如len(),reversed()等等。
也可能有些时候你不想懒惰的评估(例如先做所有的计算,以便释放资源)。 在这种情况下,列表expression可能会更好。
简介,简介,简介。
分析你的代码是唯一的方法来知道你在做什么有什么影响。
xrange,生成器等的大多数用法超过了静态大小,小数据集。 只有当你得到大量的数据集,它才真正有所作为。 range()与xrange()大体上只是使代码看起来更加丑陋,不会丢失任何东西,也许会获得一些东西。
简介,简介,简介。
你绝对不应该喜欢zip
over izip
, range
超过xrange
,或者列举对发生器理解的理解。 在Python 3.0 range
有izip
xrange
的语义, zip
有izip
的语义。
列表list(frob(x) for x in foo)
实际上更清晰,就像list(frob(x) for x in foo)
,你需要一个实际的列表。
正如你所提到的,“这对于大型数据集特别有用”,我认为这回答了你的问题。
如果你不打墙,性能明智,我会坚持列表和标准function。 那么当你遇到性能问题时就切换。
关于性能:如果使用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
就性能而言,我想不出任何时候你会想要在一个生成器上使用一个列表。
我从来没有发现发电机会阻碍你想要做的事情。 然而,有很多情况下,使用发电机不会帮助你,而不是使用它们。
例如:
sorted(xrange(5))
不提供任何改进:
sorted(range(5))
你应该更喜欢列表推导,如果你需要保留其他值的东西,你的集合的大小不是太大。
例如:您正在创build一个列表,您将在程序中多次循环。
在某种程度上,您可以将生成器视为迭代(循环)与列表parsing的替代,作为数据结构初始化的一种types。 如果你想保持数据结构,然后使用列表parsing。