Python 为什么我要用itertools.product获得MemoryError?

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 =

我希望下面的代码片段能为我提供一个迭代器,从两个输入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 = 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您找到过吗?