Python 为什么我要用itertools.product获得MemoryError?
我希望下面的代码片段能为我提供一个迭代器,从两个输入iterables的笛卡尔乘积中生成一对:Python 为什么我要用itertools.product获得MemoryError?,python,itertools,Python,Itertools,我希望下面的代码片段能为我提供一个迭代器,从两个输入iterables的笛卡尔乘积中生成一对: $ python Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53) [GCC 4.5.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import itertools >>> one =
$ python
Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53)
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import itertools
>>> one = xrange(0, 10**9)
>>> two = (1,)
>>> prods = itertools.product(one, two)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
MemoryError
$python
Python 2.7.1+(r271:868321911年4月11日,18:13:53)
[GCC 4.5.2]关于linux2
有关详细信息,请键入“帮助”、“版权”、“信用证”或“许可证”。
>>>进口itertools
>>>一个=X范围(0,10**9)
>>>二=(1,)
>>>prods=itertools.product(一、二)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
记忆者
相反,我得到了一个
内存错误
。但是我认为它没有将中间结果存储在内存中,那么是什么导致了内存错误呢?itertools.product
没有将中间结果存储在内存中,但它确实存储了原始迭代器的元组版本
通过查看itertools
模块的源代码可以看出这一点。它位于Python 2.7.2源代码发行版的文件Modules/itertoolsmodule.c
中。在函数product\u new
(基本上是product
对象的构造函数)中,我们发现从第1828行开始:
for (i=0; i < nargs ; ++i) {
PyObject *item = PyTuple_GET_ITEM(args, i);
PyObject *pool = PySequence_Tuple(item);
if (pool == NULL)
goto error;
PyTuple_SET_ITEM(pools, i, pool);
indices[i] = 0;
}
for(i=0;i
在该代码中,args
是product
的参数。在这段代码的第三行中,i
th参数被转换为一个元组。因此,代码尝试将迭代器xrange(0,10**9)
转换为元组,从而产生内存错误
我不知道为什么itertools.product
会有这样的行为。与其将每个输入迭代器存储为元组,还不如存储从每个迭代器返回的最后一项。(编辑:查看某事物的答案了解原因)我认为问题可能是xrange返回它自己的特殊类型的对象,这不是一个正常的iterable
xrange的实现方式(如列表)可以在对象上多次迭代,
而您只能在普通生成器对象上迭代一次。因此,可能是这个功能中的某些东西导致了内存错误 它不存储中间结果,但它必须存储输入值,因为对于多个输出值,每个输入值可能需要多次
由于只能在迭代器上迭代一次,product
的实现不能等同于此:
def prod(a, b):
for x in a:
for y in b:
yield (x, y)
如果此处b
是一个迭代器,它将在外循环的第一次迭代后耗尽,并且在后续执行for y in b
时不会产生更多的元素
product
通过存储b
生成的所有元素来解决此问题,以便重复使用:
def prod(a, b):
b_ = tuple(b) # create tuple with all the elements produced by b
for x in a:
for y in b_:
yield (x, y)
事实上,product
试图存储所有给定的iterables生成的元素,即使第一个参数可以避免这种情况。该函数只需遍历第一个iterable一次,因此不必缓存这些值。但它无论如何都会尝试这样做,这会导致内存错误
,这很有趣。我想对于这么简单的事情,我可以构建自己的生成器。感谢您填写实现的动机。我想解决这个问题的唯一其他方法就是坚持提供的iterables也可以被复制。我找到了问题的根源。但是,如果一个人确实需要product()的功能,那么解决方法是什么呢?@DSR您找到过吗?