首页 > 解决方案 > 列表列表的内存问题

问题描述

我有一些内存问题,我想知道是否有任何方法可以在下面的代码中释放一些内存。我尝试使用生成器表达式而不是列表推导,但这不会产生唯一的组合,因为内存已被释放。

列表(组合)列表导致我内存不足并且程序无法完成。

最终结果将是此列表中的 729 个列表,每个列表包含 6 个WindowsPath指向图像的元素。我尝试将列表作为字符串存储在文本文件中,但我无法让它工作,我尝试使用 apandas dataframe但我无法让它工作。想出一个不同的解决方案,我束手无策。现在的输出正是我需要的,但内存是唯一的问题。

from pathlib import Path
from random import choice
from itertools import product
from PIL import Image
import sys

def combine(arr):
    return list(product(*arr))

def generate(x):

    #set new value for name
    name = int(x)

    #Turn name into string for file name
    img_name = str(name)

    #Pick 1 random from each directory, add to list.
    a_paths = [choice(k) for k in layers]

    #if the length of the list of unique combinations is equal to the number of total combinations, this function stops
    if len(combinations) == len(combine(layers)):
        print("Done")
        sys.exit()

    else:
        #If combination exists, generate new list
        if any(j == a_paths for j in combinations) == True:
            print("Redo")
            generate(name)

        #Else, initialize new image, paste layers + save image, add combination to list, and generate new list
        else:
            #initialize image
            img = Image.new("RGBA", (648, 648))
            png_info = img.info

            #For each path in the list, paste on top of previous, sets image to be saved
            for path in a_paths:
                layer = Image.open(str(path), "r")
                img.paste(layer, (0, 0), layer)

            print(str(name) + ' - Unique')
            img.save(img_name + '.png', **png_info)
            combinations.append(a_paths)
            name = name - 1
            generate(name)

'''
Main method
'''
global layers
layers = [list(Path(directory).glob("*.png")) for directory in ("dir1/", "dir2/", "dir3/", "dir4/", "dir5/", "dir6/")]

#name will dictate the name of the file output(.png image) it is equal to the number of combinations of the image layers
global name
name = len(combine(layers))

#combinations is the list of lists that will store all unique combinations of images
global combinations
combinations = []

#calling recursive function
generate(name)

标签: pythonlistmemorymemory-management

解决方案


让我们从您的代码的 MRE 版本开始(即我可以在不需要一堆 PNG 的情况下运行的东西——我们在这里关心的是如何在不达到递归限制的情况下浏览图像):

from random import choice
from itertools import product


def combine(arr):
    return list(product(*arr))


def generate(x):

    # set new value for name
    name = int(x)

    # Turn name into string for file name
    img_name = str(name)

    # Pick 1 random from each directory, add to list.
    a_paths = [choice(k) for k in layers]

    # if the length of the list of unique combinations is equal to the number of total combinations, this function stops
    if len(combinations) == len(combine(layers)):
        print("Done")
        return

    else:
        # If combination exists, generate new list
        if any(j == a_paths for j in combinations) == True:
            print("Redo")
            generate(name)

        # Else, initialize new image, paste layers + save image, add combination to list, and generate new list
        else:
            # initialize image
            img = []

            # For each path in the list, paste on top of previous, sets image to be saved
            for path in a_paths:
                img.append(path)

            print(str(name) + ' - Unique')
            print(img_name + '.png', img)
            combinations.append(a_paths)
            name = name - 1
            generate(name)


'''
Main method
'''
global layers
layers = [
    [f"{d}{f}.png" for f in ("foo", "bar", "baz", "ola", "qux")]
    for d in ("dir1/", "dir2/", "dir3/", "dir4/", "dir5/", "dir6/")
]


# name will dictate the name of the file output(.png image) it is equal to the number of combinations of the image layers
global name
name = len(combine(layers))

# combinations is the list of lists that will store all unique combinations of images
global combinations
combinations = []

# calling recursive function
generate(name)

当我运行它时,我得到一些以以下开头的输出:

15625 - Unique
15625.png ['dir1/qux.png', 'dir2/bar.png', 'dir3/bar.png', 'dir4/foo.png', 'dir5/baz.png', 'dir6/foo.png']
15624 - Unique
15624.png ['dir1/baz.png', 'dir2/qux.png', 'dir3/foo.png', 'dir4/foo.png', 'dir5/foo.png', 'dir6/foo.png']
15623 - Unique
15623.png ['dir1/ola.png', 'dir2/qux.png', 'dir3/bar.png', 'dir4/ola.png', 'dir5/ola.png', 'dir6/bar.png']
...

并以 . 结尾RecursionError。我认为这就是您说“内存不足”时的意思-实际上,我似乎并没有接近内存不足(如果我有实际图像,这可能会表现不同?) ,但是 Python 的堆栈深度是有限的,并且这个函数似乎没有特别好的理由递归到任意深度。

由于您正在尝试最终生成所有可能的组合,因此您已经有了一个非常好的解决方案,您甚至已经在使用 -- itertools.product。您所要做的就是遍历它为您提供的组合。您不需要递归,也不需要全局变量。

from itertools import product
from typing import List


def generate(layers: List[List[str]]) -> None:
    for name, a_paths in enumerate(product(*layers), 1):
        # initialize image
        img = []

        # For each path in the list, paste on top of previous,
        # sets image to be saved
        for path in a_paths:
            img.append(path)

        print(f"{name} - Unique")
        print(f"{name}.png", img)

    print("Done")


'''
Main method
'''
layers = [
    [f"{d}{f}.png" for f in ("foo", "bar", "baz", "ola", "qux")]
    for d in ("dir1/", "dir2/", "dir3/", "dir4/", "dir5/", "dir6/")
]

# calling iterative function
generate(layers)

现在我们得到了所有的组合——命名从 1 开始一直到 15625:

1 - Unique
1.png ['dir1/foo.png', 'dir2/foo.png', 'dir3/foo.png', 'dir4/foo.png', 'dir5/foo.png', 'dir6/foo.png']
2 - Unique
2.png ['dir1/foo.png', 'dir2/foo.png', 'dir3/foo.png', 'dir4/foo.png', 'dir5/foo.png', 'dir6/bar.png']
3 - Unique
3.png ['dir1/foo.png', 'dir2/foo.png', 'dir3/foo.png', 'dir4/foo.png', 'dir5/foo.png', 'dir6/baz.png']
...
15623 - Unique
15623.png ['dir1/qux.png', 'dir2/qux.png', 'dir3/qux.png', 'dir4/qux.png', 'dir5/qux.png', 'dir6/baz.png']
15624 - Unique
15624.png ['dir1/qux.png', 'dir2/qux.png', 'dir3/qux.png', 'dir4/qux.png', 'dir5/qux.png', 'dir6/ola.png']
15625 - Unique
15625.png ['dir1/qux.png', 'dir2/qux.png', 'dir3/qux.png', 'dir4/qux.png', 'dir5/qux.png', 'dir6/qux.png']
Done

将实际的图像生成代码替换回我的模拟版本作为练习留给读者。

如果您想随机化组合的顺序,这样做是非常合理的:

from random import shuffle

...

    combinations = list(product(*layers))
    shuffle(combinations)
    for name, a_paths in enumerate(combinations, 1):
        ...

这会使用更多内存(因为现在您正在构建list产品而不是通过生成器进行迭代),但是您正在使用的图像数量实际上并没有那么大,所以只要您不是这样就可以了t 为每个图像添加一个递归级别。


推荐阅读