【问题标题】:Memory issues with a list of lists [closed]列表列表的内存问题[关闭]
【发布时间】:2021-10-18 02:48:59
【问题描述】:

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

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

最终结果将是此列表中的 729 个列表,每个列表包含 6 个指向图像的 WindowsPath 元素。我尝试将列表作为字符串存储在文本文件中,但我无法让它工作,我尝试使用pandas 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)

【问题讨论】:

  • 看来问题出在您发布的代码之外。 layerscombinations 是在哪里生成的? combine 是做什么的?这段代码的总体目的是什么?如果您尝试通过所有可能的组合,您可以使用itertools 的生成器之一来提供。如果您需要使用随机生成技术,您可能可以通过存储哈希而不是全部内容来避免受骗,但是在不知道您正在使用的值的类型的情况下,很难建议您如何这样做。
  • @Samwise 我添加了其余的代码。抱歉没有指定。我会澄清一些事情。这段代码的总体目的是从每个目录(dir1、dir2、dir3 等)中随机选择 1 张图像,并将每个图像粘贴在一起。每个目录中有 3 个图像可供选择。随机选择图像然后与称为“组合”的列表进行比较,如果选择的图像组合存在,则再次调用该函数。如果没有,它会将组合(这是一个列表)添加到名为“combinations”的列表中并再次调用该函数。
  • @Samwise 另一个更新,我尝试做 b_paths = hash(str(a_paths)), b_paths = str(b_paths),然后比较哈希并将哈希存储在列表中。同样的结果,在我的内存占用了大约 80% 的 RAM 后,功能会自行关闭。我到达那里大约 95% 的路。如果有办法我可以清除一些正在存储的内存或覆盖我的内存使用以在切断之前允许更多的时间,我将能够完成输出。
  • 所以看起来你正在迭代,直到你在列表中生成了每个可能的组合。 为什么不直接遍历组合,而不是将它们保存为列表,然后生成随机组合以重新发现它们?

标签: python list memory memory-management


【解决方案1】:

让我们从您的代码的 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 而不是通过生成器进行迭代),但是您正在使用的图像数量实际上并没有那么大,所以这很好只要您不为每个图像添加递归级别。

【讨论】:

  • 这绝对是我一直在寻找的解决方案,由于递归而将图像保留在内存中绝对是问题所在。我很感激,你是我的救星!洗牌包绝对是我希望包括的!谢谢!!!!
  • 只是想知道,您是否知道是否有任何方法可以让目录中的 1 个图像显示在一定百分比的输出中?例如:60% 的时间生成来自 dir1 的图像 1,30% 的时间生成来自 dir1 的图像 2,在剩余的 10% 的所有输出图像中生成来自 dir1 的图像 3。
  • 如果您的意图是详尽地生成所有组合,并且没有任何重复,那么根据定义,这是不可能的。如果您想按照您的描述更改分布,您需要愿意满足这两个要求之一。 :)
  • 我是这么想的哈哈。好吧,一点点手动删除就可以了。我永远感谢您的帮助
  • 对你有用的工具是random.choices,特别是weights 参数,它可以让你一举指定百分比——你只需要弄清楚你是否想要添加额外的图像(这将是现有图像的副本)或删除一些。
猜你喜欢
  • 2021-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-13
  • 2016-12-20
  • 1970-01-01
相关资源
最近更新 更多