将Python生成器解包为参数 - 节省内存吗?
假设我有一个生成器,用来生成集合
:
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])
现在我想把这些集合合并在一起,也就是做并集
。我可以先创建一个临时的集合列表,然后把这个列表里的内容传给并集
:
>>> set.union( * list( f(5) ) )
set([0, 1, 2, 3])
我也可以直接把生成器传给并集
:
>>> set.union( * f(5) )
set([0, 1, 2, 3])
那么第二种方法会像第一种那样创建一个完整的临时列表吗?哪种方法更节省内存呢?
2 个回答
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
注意到“Blah!”没有被打印出来。因为在尝试计算生成器 f(5)
中的所有元素时发生了异常,所以 blah
这个调用根本没有执行。
11
在Python中,当你把生成器作为参数使用时,它会先展开生成器;这意味着在调用之前,生成器产生的所有值都会被加载到内存中,无论你选择哪种方式。
你也可以使用reduce()
函数来代替:
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])