这与你可以使用Python生成器函数有什么相反?:python生成器,生成器表达式和itertools
模块是我最近python的一些特性.它们在设置操作链以在大量数据上执行时特别有用 - 我经常在处理DSV文件时使用它们.
那么什么时候不是使用生成器,生成器表达式或itertools
函数的好时机?
当我应该喜欢zip()
过itertools.izip()
,或
range()
结束xrange()
,或
[x for x in foo]
结束(x for x in foo)
?
显然,我们最终需要将生成器"解析"为实际数据,通常是通过创建列表或使用非生成器循环对其进行迭代.有时我们只需知道长度.这不是我要问的.
我们使用生成器,因此我们不会将新列表分配给内存以用于临时数据.这对于大型数据集尤其有用.对于小型数据集也有意义吗?有明显的内存/ CPU权衡吗?
考虑到列表理解性能与map()和filter()的开放性讨论,我特别感兴趣的是,如果有人对此做了一些分析.(alt链接)
在以下情况下使用列表而不是生成器:
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,有时无法通过正常的函数调用和列表操作来优化生成器代码.
通常,在需要列表操作时不要使用生成器,例如len(),reversed()等.
有时您可能不想进行延迟评估(例如,预先进行所有计算以便释放资源).在这种情况下,列表表达式可能会更好.
简介,简介,简介.
分析代码是了解您所做的事情是否有任何影响的唯一方法.
xrange,generator等的大多数用法都是静态大小的小数据集.只有当你到达大型数据集时才会真正发挥作用.range()vs. xrange()主要是让代码看起来更丑陋,不会丢失任何东西,也许会获得一些东西.
简介,简介,简介.
你不应该偏向zip
过izip
,range
过xrange
,或者在发电机链表推导.在Python 3.0 range
有xrange
样的语义和zip
有izip
样的语义.
列表理解实际上更清晰,就像list(frob(x) for x in foo)
那些需要实际列表的时候一样.
正如你所提到的,"这对于大型数据集特别有意义",我认为这可以回答你的问题.
如果你没有在性能方面击中任何墙壁,你仍然可以坚持列表和标准功能.然后当遇到性能问题时进行切换.
正如评论中@ u0b34a0f6ae所提到的,在开始时使用生成器可以使您更容易扩展到更大的数据集.
关于性能:如果使用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