嵌套python生成器的速度有多快?
好的,所以我可能不应该担心这个问题,但是我有一些代码,可以通过一组过滤器、映射和其他东西传递一个(可能很长,可能很短)可能性列表,我想知道我的实现是否会执行得很好嵌套python生成器的速度有多快?,python,generator,Python,Generator,好的,所以我可能不应该担心这个问题,但是我有一些代码,可以通过一组过滤器、映射和其他东西传递一个(可能很长,可能很短)可能性列表,我想知道我的实现是否会执行得很好 作为我想做的事情的一个例子,考虑一下这个操作链: 获取从1到100的所有数字 只保留偶数 将每个数字平方 生成上面列表中的i和[1,2,3,4,5]中的j的所有对[i,j] 仅保留i+j>40的配对 现在,在做了所有这些废话之后,我想通过这组对[I,j]来寻找一个满足一定条件的对。通常,解决方案是第一个条目之一,在这种情况下,我甚
作为我想做的事情的一个例子,考虑一下这个操作链:
- 获取从1到100的所有数字
- 只保留偶数
- 将每个数字平方
- 生成上面列表中的i和[1,2,3,4,5]中的j的所有对[i,j]
- 仅保留i+j>40的配对
我的问题是:在我必须遍历所有可能的情况下,一系列嵌套生成器是否会对性能造成重大影响?根据官方文档,使用生成器表达式基本上等同于调用
imap
,因为它创建了一个迭代器。(“”)没有明确讨论嵌套表达式是创建单独(组合)的对象,还是创建内部逻辑复杂的单个表达式,但假设我是解释器实现者,嵌套对象似乎是实现嵌套生成器表达式的最直接的方法
然而,在决定哪些表现更好时,还有其他因素在起作用。我了解到,最小化短期对象的创建是影响性能的一个重要因素,在Python中,这样做有时很难注意到
性能差:(f(x)表示范围(100)内的x)#生成100个元素列表
更好的性能:(f(x)表示x范围内的x(100))#使用计数迭代器
我在自己的实现中一直使用itertools
模块中的imap
、ifilter
和izip
,我发现它们性能良好。虽然每次调用它们都会创建一个新的迭代器对象,但这是相当轻量级的,有点像一个列表,其中从不包含多个项。此外,在CPython中,这些都是用C实现的,因此非常高效
在封面下,用纯Python实现的迭代器有一个next
方法,调用该方法来检索每个数据。方法调用的成本不是很大,但也不是零。因此,如果您的代码将在一个必须尽可能优化的紧密循环中使用,下面是我的建议:
- 在可能的情况下,一定要使用
、imap
和ifilter
,而不是izip
、map
和filter
,它们在内存中构建结果列表并返回结果。如果您有使用基于列表的版本的代码,那么通过更改为基于迭代器的版本,您将看到一个很大的改进zip
模块包含其他函数,如itertools
、takewhile
、starmap
和chain
,这些函数在链式迭代器实现中通常很有用chain.from.\u iterable
- 与其链接
的多个应用程序,不如尽可能组合传入的函数。例如,将过滤器组合为ifilter
,而不是ifilter(lambda v:v>0,ifilter(lambda v:v%3==0,数据)
。在某些情况下,重新排列操作顺序可能是有效的,这样您就可以按这种方式折叠它们ifilter(lambda v:v>0)和(v%3==0,数据)
- 当您为了实现副作用而执行映射操作,并且对结果不感兴趣时,可以使用此操作而不是
,以避免结果在内存中累积:map
def consume(i): u'eat all contents of given iterator' while True: i.next() consume(imap(side_effect, data))
def foo(data):
return reduce(R, imap(bar, ifilter(lambda v: v % 5 == 0, data)))
一种修复方法(此方法每次仍将创建两个迭代器对象,这是必需的,但不是附加的lambda表达式):
(注意:答案适用于Python2。在Python3中,
map
,filter
和zip
返回迭代器。)根据官方文档,使用生成器表达式基本上等同于调用imap
,因为它创建了迭代器。(“”)没有明确讨论嵌套表达式是创建单独(组合)的对象,还是创建内部逻辑复杂的单个表达式,但假设我是解释器实现者,嵌套对象似乎是实现嵌套生成器表达式的最直接的方法
然而,在决定哪些表现更好时,还有其他因素在起作用。我了解到,最小化短期对象的创建是影响性能的一个重要因素,在Python中,这样做有时很难注意到
性能差:(f(x)表示范围(100)内的x)#生成100个元素列表
更好的性能:(f(x)代表x范围内的x
_mod5zero = lambda v: v % 5 == 0
def foo(data):
return reduce(R, imap(bar, ifilter(_mod5zero, data)))
def foo1():
data = ((a,b) for a in (i*i for i in xrange(1,101) if i%2) for b in [1,2,3,4,5] if a+b > 40)
return list(data)
def foo2():
result=[]
for i in range(1,101):
if i%2:
i=i*i
for j in [1,2,3,4,5]:
if i+j > 40:
result+=[(i,j)]
return result
def foo3():
data=[(a,b) for a in (i*i for i in range(1,101)) for b in [1,2,3,4,5] if a+b > 40]
return data
def foo4():
data=[(a,b) for a in [i*i for i in range(1,101)] for b in [1,2,3,4,5] if a+b > 40]
return data
>>> t1=timeit.Timer("foo1()","from __main__ import foo1")
>>> t2=timeit.Timer("foo2()","from __main__ import foo2")
>>> t3=timeit.Timer("foo3()","from __main__ import foo3")
>>> t4=timeit.Timer("foo4()","from __main__ import foo4")
>>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=10000)/10000)
100.95 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=10000)/10000)
158.90 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t3.timeit(number=10000)/10000)
130.02 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t4.timeit(number=10000)/10000)
133.68 usec/pass
>>>