Skip to main content
 首页 » 编程设计

python之嵌套的 python 生成器有多快

2025年05月04日18dudu

好吧,所以我可能不应该担心这个,但我有一些代码是为了通过一组过滤器和 map 传递一个(可能很长,可能很短)可能性列表,并且其他事情,我想知道我的实现是否会表现良好。

作为我想做的事情的一个例子,考虑这个操作链:

  • 获取从 1 到 100 的所有数字
  • 只保留偶数
  • 将每个数字平方
  • 用上面列表中的 i 和 [1, 2, 3, 4,5] 中的 j 生成所有对 [i, j]
  • 只保留 i + j > 40 的对

现在,在做了所有这些废话之后,我想通过这组对 [i, j] 来寻找满足特定条件的对。通常,解决方案是第一个条目之一,在这种情况下,我什至不看任何其他条目。然而,有时我必须使用整个列表,但找不到答案而不得不抛出错误。

我想将我的“操作链”实现为一系列生成器,即每个操作迭代前一个生成器生成的项目,并逐项“产生”自己的输出(SICP 流)。这样,如果我从不查看输出的最后 300 个条目,它们甚至不会得到处理。我知道 itertools 提供了 imap 和 ifilter 之类的东西来执行我想要执行的许多类型的操作。

我的问题是:在我必须遍历所有可能性的情况下,一系列嵌套生成器是否会对性能产生重大影响?

请您参考如下方法:

我尝试了两种实现方式,一种使用生成器,一种不使用生成器。我在 2.7 中对其进行了测试,因此 range 返回一个列表而不是迭代器。

这里是实现

使用生成器

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 
>>>  

结论:

生成器表达式非常强大,您可以对其进行更大程度的优化。正如您在示例 foo2 中看到的那样,这是最慢的,它很难附加单个列表,从而降低了性能。 foo3 和 foo4 的时间几乎相同,所以创建临时列表似乎不是瓶颈,因为它在整个迭代中只创建了一次。如果没有生成器,您很快就会遇到一些性能问题,例如附加列表或创建临时列表。因此惰性求值应运而生,以克服这些性能瓶颈。