Python: 如何将生成器传递给函数?

1 投票
3 回答
2308 浏览
提问于 2025-04-18 15:31

简要描述:

你可以通过看看这个(有点问题的)代码片段来猜测我想做什么:

numpy.savez('tmp.npz',range(i) for i in range(2,100))

我不太想用一些变通的方法,比如:

numpy.save('tmp.npy',[range(i) for i in range(2,100)])

因为这样的话,整个嵌套列表必须同时生成并保存在内存中,而不是每个子列表生成后就传递,然后再从内存中清除。

正式/详细描述:

假设我有一个函数 f,它可以接受任意数量的参数,而这些参数都是列表:

def f(*args,**kwargs):  #all arguments are (non nested) lists.
    ...

但我不能(也不想)改变函数 f 本身;而且我有一个生成器可以产生很多列表:

nested=[<generator G, producing many lists>]

其中 <G> 可能是像 range(10) for i in range(10) 这样的东西。

我该如何把这些列表传递给这个函数呢?无论是 f([<G>]) 还是 f(<G>) 都不行。

3 个回答

-1

()把一个理解式包起来,而不是用[],这样就会变成一个生成器理解式。

0

如果你查看一下numpy的输入输出源代码,你会发现savez(file, *args, **kwds)其实只是一个包装器,它会调用_savez(file, args, kwds, compress)这个函数。_savez()接收的是args而不是*args,这意味着你可以传递一个生成器,这个生成器会在保存的时候被使用,而不是在调用的时候就被消耗掉。

我没有测试过,但像这样的代码应该是可以工作的:

from numpy.lib.npyio import _savez

_savez('tmp.npz', (range(i) for i in range(2,100)), {}, False)
1

我觉得你想要的是:

np.savez("tmp.npz", *(np.arange(i) for i in range(2, 100)))

注意这里使用了 * 来把数组生成器的内容“展开”成函数的参数。我把外层的范围改成了 xrange,这样可以避免生成一个完整的列表,而内层的范围则用 np.arange 来创建数组。

>>> np.savez("tmp.npz", *(np.arange(i) for i in xrange(2, 100)))
>>> np.load("tmp.npz")['arr_60']
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
       34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
       51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61])

撰写回答