上限内存是多少?

32

Python是否有内存限制?我一直在使用Python脚本计算一个至少150mb的文件中的平均值。

根据文件大小,有时会出现MemoryError错误。

可以为Python分配更多内存以避免出现错误吗?


编辑:现在附上代码

注意:文件大小可能差异很大(高达20GB),最小文件大小为150mb。

file_A1_B1 = open("A1_B1_100000.txt", "r")
file_A2_B2 = open("A2_B2_100000.txt", "r")
file_A1_B2 = open("A1_B2_100000.txt", "r")
file_A2_B1 = open("A2_B1_100000.txt", "r")
file_write = open ("average_generations.txt", "w")
mutation_average = open("mutation_average", "w")

files = [file_A2_B2,file_A2_B2,file_A1_B2,file_A2_B1]

for u in files:
    line = u.readlines()
    list_of_lines = []
    for i in line:
        values = i.split('\t')
        list_of_lines.append(values)

    count = 0
    for j in list_of_lines:
        count +=1

    for k in range(0,count):
        list_of_lines[k].remove('\n')

    length = len(list_of_lines[0])
    print_counter = 4

    for o in range(0,length):
        total = 0
        for p in range(0,count):
            number = float(list_of_lines[p][o])
            total = total + number
        average = total/count
        print average
        if print_counter == 4:
            file_write.write(str(average)+'\n')
            print_counter = 0
        print_counter +=1
file_write.write('\n')

2
你能给我们展示一下你的脚本吗?我在Python中处理过更大的文件,没有出现任何问题。 - robert
1
你的脚本想要做什么?在我看来,你想要计算每个输入文件中每四列的平均值。是这样吗? - Tim Pietzcker
我注意到在Windows(XP)和OS X / Linux上运行相同的Python应用程序时,关于内存方面存在显着的性能差异。 Windows端的性能往往是最差的。 - SW_user2953243
5个回答

34

(这是我的第三个答案,因为我误解了您的代码在原始代码中的作用,然后在第二次中犯了一个小但至关重要的错误—希望第三次能成功。

编辑: 由于这似乎是一个流行的答案,我进行了一些修改以改进其实现多年来的情况—大多数不太重要。这样,如果人们将其用作模板,它将提供更好的基础。

正如其他人所指出的那样,您的MemoryError问题很可能是因为您试图将巨大文件的整个内容读入内存,然后在此基础上通过创建每行字符串值的列表的列表有效地将所需内存量加倍。

Python的内存限制取决于计算机和操作系统可用的物理RAM和虚拟内存磁盘空间。即使您没有使用所有内存并且程序“工作”,使用它也可能是不切实际的,因为它需要太长时间。

无论如何,避免这种情况最明显的方法是一次处理单个文件,这意味着您必须逐步进行处理。

为了实现这一点,将保持每个字段的运行总数列表。完成后,可以通过将相应的总值除以读取的总行数来计算每个字段的平均值。一旦完成了这些操作,这些平均值就可以打印出来,并写入其中一个输出文件。我还有意识地努力使用非常描述性的变量名称来尝试使其易于理解。

try:
    from itertools import izip_longest
except ImportError:    # Python 3
    from itertools import zip_longest as izip_longest

GROUP_SIZE = 4
input_file_names = ["A1_B1_100000.txt", "A2_B2_100000.txt", "A1_B2_100000.txt",
                    "A2_B1_100000.txt"]
file_write = open("average_generations.txt", 'w')
mutation_average = open("mutation_average", 'w')  # left in, but nothing written

for file_name in input_file_names:
    with open(file_name, 'r') as input_file:
        print('processing file: {}'.format(file_name))

        totals = []
        for count, fields in enumerate((line.split('\t') for line in input_file), 1):
            totals = [sum(values) for values in
                        izip_longest(totals, map(float, fields), fillvalue=0)]
        averages = [total/count for total in totals]

        for print_counter, average in enumerate(averages):
            print('  {:9.4f}'.format(average))
            if print_counter % GROUP_SIZE == 0:
                file_write.write(str(average)+'\n')

file_write.write('\n')
file_write.close()
mutation_average.close()

7
(a)这篇文章的作者并没有“试图一次性将多个大文件读入内存”;他是一个接一个地读取它们的。 (b)然而,作者在读取每个文件时将每个文件占用的内存翻倍了[请参见我的答案]。 (c)您的代码行不通;totalsfieldstr对象;我们需要数值总数来计算平均数;您的总数将会变成非常长的字符串;这是Python,不是awk;您需要添加一些float()函数。 (d)totals = [field for field in fields]而不是totals = fields - John Machin
3
那么,答案是“没有强制性的内存限制”吗? - ThorSummoner
@ThorSummoner:从第三段可以得知:“Python的内存限制取决于您的计算机和操作系统可用的物理RAM和虚拟内存磁盘空间。” - martineau
@martineau [防御性地] 当然,所有进程都受到同样的限制。因此,这不是Python的限制,而是运行Python的系统的限制!在我看来,它没有明确回答明确的问题。 - ThorSummoner
@ThorSummoner:我不知道,但在我看来,它直接回答了这个问题。尽管我没有提到它还受解释器是32位还是64位版本的固有限制。 - martineau
显示剩余2条评论

20
你正在将整个文件读入内存中(line = u.readlines()),如果文件太大(你说有些文件达到20GB),这显然会失败,所以这就是你的问题所在。
最好逐行迭代:
for current_line in u:
    do_something_with(current_line)

这是推荐的方法。

在脚本的后面,您正在做一些非常奇怪的事情,比如首先计算列表中所有项目的数量,然后构建一个for循环来遍历该计数的范围。为什么不直接遍历列表?你的脚本的目的是什么?我有印象这可以更容易地完成。

这是高级语言(如Python)的优点之一(与您必须自己处理这些任务的C语言相反):允许Python为您处理迭代,并且仅在任何给定时间收集您实际需要在内存中的内容。

此外,由于似乎您正在处理TSV文件(制表符分隔值),因此您应该查看csv模块,它将为您处理所有拆分、删除\n等操作。


18

Python 可以使用其环境中可用的所有内存。我的简单的“内存测试”在使用约

1959167 [MiB]

在Jython 2.5上,它会更早地崩溃:

 239000 [MiB]

可能我可以配置Jython以使用更多的内存(它使用来自JVM的限制)

测试应用:

import sys

sl = []
i = 0
# some magic 1024 - overhead of string object
fill_size = 1024
if sys.version.startswith('2.7'):
    fill_size = 1003
if sys.version.startswith('3'):
    fill_size = 497
print(fill_size)
MiB = 0
while True:
    s = str(i).zfill(fill_size)
    sl.append(s)
    if i == 0:
        try:
            sys.stderr.write('size of one string %d\n' % (sys.getsizeof(s)))
        except AttributeError:
            pass
    i += 1
    if i % 1024 == 0:
        MiB += 1
        if MiB % 25 == 0:
            sys.stderr.write('%d [MiB]\n' % (MiB))
在你的应用程序中,你一次性读取整个文件。对于这样大的文件,你应该逐行读取。

5
我在我的电脑(win7-64, python27, 16GB内存)上运行了你的脚本,使用了1900 [MiB]后就崩溃了,但是从任务管理器中我知道可用的物理内存约为8000M。因此,“Python可以使用其环境中的所有可用内存”可能并不正确。 - lengxuehx
5
我错了。它崩溃的原因是在Windows系统中,默认32位进程只能使用2GB的内存限制。 - lengxuehx
1
有人知道2018年Windows Python安装程序的默认位数是32位吗? - Elliot

10
不,Python应用程序的内存使用没有特定的限制。我经常处理使用几个GB内存的Python应用程序。很可能,您的脚本实际上使用的内存超出了您正在运行的计算机上可用的内存。
在这种情况下,解决方案是重写脚本以提高内存效率,或者如果脚本已经优化以最小化内存使用,则添加更多物理内存。
编辑:
您的脚本一次将文件的全部内容读入内存中(line = u.readlines())。由于您正在处理大小达20 GB的文件,因此除非您的计算机具有大量内存,否则会出现内存错误。
更好的方法是逐行读取文件。
for u in files:
     for line in u: # This will iterate over each line in the file
         # Read values from the line, do necessary calculations

6

你不仅将每个文件的全部内容读入内存,还费力地将信息复制到一个名为list_of_lines的表中。

你还有一个次要问题:你选择的变量名称严重混淆了你正在做的事情。

以下是已删除readlines()操作并使用有意义名称重写的脚本:

file_A1_B1 = open("A1_B1_100000.txt", "r")
file_A2_B2 = open("A2_B2_100000.txt", "r")
file_A1_B2 = open("A1_B2_100000.txt", "r")
file_A2_B1 = open("A2_B1_100000.txt", "r")
file_write = open ("average_generations.txt", "w")
mutation_average = open("mutation_average", "w") # not used
files = [file_A2_B2,file_A2_B2,file_A1_B2,file_A2_B1]
for afile in files:
    table = []
    for aline in afile:
        values = aline.split('\t')
        values.remove('\n') # why?
        table.append(values)
    row_count = len(table)
    row0length = len(table[0])
    print_counter = 4
    for column_index in range(row0length):
        column_total = 0
        for row_index in range(row_count):
            number = float(table[row_index][column_index])
            column_total = column_total + number
        column_average = column_total/row_count
        print column_average
        if print_counter == 4:
            file_write.write(str(column_average)+'\n')
            print_counter = 0
        print_counter +=1
file_write.write('\n')

很快就会显而易见,(1) 你正在计算列平均值 (2) 掩盖导致其他人认为你在计算行平均值。
由于你正在计算列平均值,因此每个文件的输出只需要在结尾时完成,并且实际所需的额外内存量与列数成比例。
以下是外部循环代码的修订版本:
for afile in files:
    for row_count, aline in enumerate(afile, start=1):
        values = aline.split('\t')
        values.remove('\n') # why?
        fvalues = map(float, values)
        if row_count == 1:
            row0length = len(fvalues)
            column_index_range = range(row0length)
            column_totals = fvalues
        else:
            assert len(fvalues) == row0length
            for column_index in column_index_range:
                column_totals[column_index] += fvalues[column_index]
    print_counter = 4
    for column_index in column_index_range:
        column_average = column_totals[column_index] / row_count
        print column_average
        if print_counter == 4:
            file_write.write(str(column_average)+'\n')
            print_counter = 0
        print_counter +=1

没什么大不了的,但是读入的值没有必要浮动到一个单独的列表中,也不需要将它们(按列)总计算为实数,只需要确保它们的平均值以该格式计算即可。 - martineau
@martineau:如果你的第一点是指values = map(float, values):我讨厌这种类型转换。第二点:列总数怎么可能不是浮点数??? - John Machin
@John Machin:我的意思是这些值可以是整数而不是浮点数。有时我认为它们已经是整数了,但现在理解了,由于它们最初是字符串,因此需要将它们转换为某种数字类型。考虑到我认为它们是整数,那么它们的总和也可能是整数--因此是第二个要点。您将其转换为“float”可能是正确的,这确实需要总和也是如此。 - martineau
逻辑问题:我认为你答案中外部循环的修订版本中所示的代码不会起作用,因为row_count1开始,所以if not row_count:初始化将永远不会被执行。 - martineau
@martineau:关于浮点数的问题,OP正在使用float()函数。感谢你发现了row_count的错误,已经修复了。 - John Machin
@John Machin:我猜我已经吃了这个苦头,永远不要在回答中发布我没测试过的Python代码(以前从未做过的事情)。这样就可以消除早期尝试中大部分的问题,并且可能也可以使用相同的测试文件来确定OP的代码实际上是什么。 - martineau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接