Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在不破坏内存的情况下复制生成器_Python_Generator_Itertools - Fatal编程技术网

Python 在不破坏内存的情况下复制生成器

Python 在不破坏内存的情况下复制生成器,python,generator,itertools,Python,Generator,Itertools,我正在编写一个python类,在给定一个整数size和一个用于可能的组合的生成器的情况下,找到所有可能的方法。这些组合是长度为size**2的元组,并被拆分为size×size网格。代码本身工作正常,但重用生成器似乎需要itertools.tee。在下面所示的示例中,这会导致线程使用的内存跳转到300MB,因为迭代器中的每个值都存储在一个列表中 from itertools import permutations, tee class MagicSquare: def __init__

我正在编写一个python类,在给定一个整数
size
和一个用于可能的
组合的生成器的情况下,找到所有可能的方法。这些组合是长度为
size**2
的元组,并被拆分为
size
×
size
网格。代码本身工作正常,但重用生成器似乎需要
itertools.tee
。在下面所示的示例中,这会导致线程使用的内存跳转到300MB,因为迭代器中的每个值都存储在一个列表中

from itertools import permutations, tee

class MagicSquare:
    def __init__(self, size, combinations):
        self.size = size
        self.range = range(self.size)
        self.combinations = combinations

    def getGrid(self, entries):
        return [ entries[self.size*i:self.size*(i+1)] for i in self.range ]

    def checkGrid(self, grid):
        check_sum = sum(grid[0])
        if any( sum(row) != check_sum for row in grid ): 
            return False
        if any( sum(row[col] for row in grid) != check_sum for col in self.range ): 
            return False
        if sum(grid[diag][diag] for diag in self.range) != check_sum: 
            return False
        if sum(grid[diag][self.size-diag-1] for diag in self.range) != check_sum: 
            return False
        return True

    def solutions(self):
        combinations, self.combinations = tee(self.combinations)
        for entries in combinations:
            grid = self.getGrid(entries)
            if self.checkGrid(grid):
                yield grid

if __name__ == '__main__':
    combs = permutations(range(20,30), 9)
    ms = MagicSquare(3, combs)
    for solution in ms.solutions():
        for row in solution:
            print row
        print
对于这个问题,我想到了两个显而易见的解决方案。首先,我可以要求提供生成器的函数,而不是要求生成器本身,但这需要用户包装其生成器表达式。其次,我可以缓存解决方案。为了便于讨论,假设如果没有足够数量的解,我不想再检查对角线,因此我需要更新
checkGrid
,并重复
组合

所以,我的问题是:真的没有办法复制一个生成器而不产生这个潜在的巨大内存问题吗?我不关心是否保留生成器的部分状态,我只希望它在与原始生成器相同的值上迭代

编辑
在Python3.X中,您可以使用
copy.deepcopy
来复制依赖项都是可拾取的
itertools
对象。

由于生成器是自包含的和确定性的,因此使用两个副本的最佳方法是创建两个副本。(如有必要,请修改
MagicSquare
的签名以接受两个生成器;但您似乎希望该副本用于其他目的?)


无法复制任意迭代器。极少数特定迭代器类型支持复制;我只知道一个是
itertools.tee
。不过,一般来说,迭代器可能有太多不可复制的依赖项,使得复制机制无法成为迭代器协议的一部分

您之所以会遇到这个问题,是因为您编写了一个API,试图使用一次性迭代器并返回一个可重用对象。如果要使用迭代器,则应将API设计为返回迭代器,而不是可以创建一次然后反复调用
solutions
MagicSquare
对象


对于您的用例,我建议您制作
MagicSquare
生成器。该类的主要用途(可能仅限于此)似乎是调用
solutions
作为解决方案的迭代器。为什么不简单地用一个函数替换该类,该函数执行
MagicSquare(大小、组合)。solutions()
当前的功能?

传递一个函数,而不是传递一个生成器,该函数在调用时返回一个新的生成器。这将允许
MagicSquare
根据需要多次迭代组合,而无需将它们保存在内存中

要解释代码,请执行以下操作:

class MagicSquare:
    def __init__(self, size, get_combinations):
        ...
        self.get_combinations = get_combinations

    ...

    def solutions(self):
        for entries in self.get_combinations():
            ...

if __name__ == '__main__':
    combs2 = lambda: permutations(range(20,30), 9) # 
    ms2 = MagicSquare(3, combs2)
    ...

没有什么是不可能的

以下内容恰好适用于
itertools.permutations
。不要假设它能与任何iterable一起工作,因为它不会

>>> from itertools import permutations
>>> combs = permutations(range(20,30), 9)
>>> from copy import deepcopy
>>> combs2 = deepcopy(combs)
>>> next(combs)
(20, 21, 22, 23, 24, 25, 26, 27, 28)
>>> next(combs2)
(20, 21, 22, 23, 24, 25, 26, 27, 28)

澄清一下,这是一个假设问题,对于一个将要处理大量
itertools
迭代器的项目来说。上面建议的两种解决方案的实现都很简单,我想知道是否绝对不可能复制
itertools.xxx
对象。但是,如果我想重复使用生成器任意或未知次数,该怎么办?您每次都会创建另一个?在您描述的一般情况下,您无法再次知道需要什么,因此无法绕过输出的完全缓存,就像
tee
所做的那样。无论您是按照公认的答案制作另一个还是克隆,都没有什么区别——如果插入新的生成器,您需要参与其中
deepcopy
很聪明,但它依赖于你的细心和(正确地)测试每一个新输入。在我的问题中,我注意到“我可以要求提供生成器的函数,而不是要求生成器本身,但这需要用户包装其生成器表达式。”似乎不可能复制迭代器。啊,抱歉,我错过了这一部分。这似乎只适用于Python 3.X,因为
copy.copy
copy.deepcopy
都在Python 2.7中抛出错误。我将使用其他
itertools
对象进行测试。您没有提到Python的特定版本,所以我假设是CPython 3.x。为了这个好处,可能值得更改,没有什么真正让我坚持使用Python 2.7,我只是没想到会有不同的行为。看起来他们在Python 3中为
itertools
迭代器添加了pickle支持,所以只要它们的依赖项是可pickle的,
itertools
迭代器将是可pickle和可深度复制的。但是,当涉及到生成器时,这不会有帮助。好吧,我的问题的答案似乎是“生成器可能不可复制,但由于
itertools
支持在Python 3.X中拾取,因此可以使用内置的
copy
模块复制这些对象。”谢谢你们!
>>> from itertools import permutations
>>> combs = permutations(range(20,30), 9)
>>> from copy import deepcopy
>>> combs2 = deepcopy(combs)
>>> next(combs)
(20, 21, 22, 23, 24, 25, 26, 27, 28)
>>> next(combs2)
(20, 21, 22, 23, 24, 25, 26, 27, 28)