将Python生成器解压缩为参数 - 内存效率高吗?

时间:2014-08-17 02:00:34

标签: python python-2.7 iterator generator

说我有sets的生成器:

def f(n) :
  for i in xrange(n) :
    yield set(xrange(i) )

>>> for s in f(5) :
      print s

set([])
set([0])
set([0, 1])
set([0, 1, 2])
set([0, 1, 2, 3])

现在我想union他们。我可以创建一个临时的集合列表,并将该列表解压缩为union的参数:

>>> set.union( * list( f(5) ) )
set([0, 1, 2, 3])

我也可以将生成器本身提供给union

>>> set.union( * f(5) )
set([0, 1, 2, 3])

第二种方法是否像第一种方法一样创建完整的临时列表?哪种方法有效记忆?

2 个答案:

答案 0 :(得分:7)

Python在将它作为参数应用时,首先展开生成器 ;在两个选项中,生成器生成的所有值都会在调用发生之前加载到内存中。

您可以改为使用reduce() function来电:

from functools import reduce  # Python 3 forward compatibility

reduce(set.union, f(5))

这将逐个迭代f(5)生成的值,而不首先构建它们的序列。

演示:

>>> def f(n):
...     for i in xrange(n):
...         yield set(xrange(i))
... 
>>> reduce(set.union, f(5))
set([0, 1, 2, 3])

答案 1 :(得分:4)

两种方法都将评估(并存储)生成器的所有元素。调用函数时,必须在调用函数之前评估整个参数列表。

您可以通过一个小例子看到这一点:

def f(n):
    for i in xrange(n) :
        yield set(xrange(i) )
    1/0

def blah(*args):
    print "Blah!"

>>> blah(*f(5))
Traceback (most recent call last):
  File "<pyshell#56>", line 1, in <module>
    blah(*f(5))
  File "<pyshell#52>", line 4, in f
    1/0
ZeroDivisionError: division by zero

请注意&#34; Blah!&#34;没有打印。由于在尝试评估生成器f(5)中的所有元素时引发了异常,因此永远不会调用blah