为Python中的列表保留内存?

Posted

技术标签:

【中文标题】为Python中的列表保留内存?【英文标题】:Reserve memory for list in Python? 【发布时间】:2010-10-06 22:19:06 【问题描述】:

在 Python 中编程时,是否可以为将填充已知数量项目的列表保留内存,以便在构建时不会多次重新分配列表?我查看了 Python 列表类型的文档,但没有找到任何似乎可以做到这一点的东西。但是,这种类型的列表构建会出现在我的代码的一些热点中,所以我想尽可能提高它的效率。

编辑:另外,用 Python 这样的语言做这样的事情是否有意义?我是一个相当有经验的程序员,但对 Python 很陌生,并且仍然对它的做事方式有所了解。 Python 是否在内部将 所有 对象分配到单独的堆空间中,从而违背了尝试最小化分配的目的,还是将 int、float 等原语直接存储在列表中?

【问题讨论】:

@ironfroggy:关键是这个出现在热点中。在这些地方,列表构建造成了现实世界的重大瓶颈,您应该优化这种瓶颈。 Python - Create a list with initial capacity 的可能重复项 【参考方案1】:

您可以像这样创建已知长度的列表:

>>> [None] * known_number

【讨论】:

【参考方案2】:

在大多数日常代码中,您不需要这种优化。

但是,当列表效率成为问题时,您应该做的第一件事是将通用列表替换为来自array module 的类型列表,这样效率更高。

创建 400 万个浮点数列表的方法如下:

import array
lst = array.array('f', [0.0]*4000*1000)

【讨论】:

您所说的“效率更高”是什么意思? array.array 可能需要更少的内存,但在大多数情况下(即我尝试过的情况),Python 列表更快。 在这种情况下,它甚至首先创建一个列表,然后从列表中创建一个数组。这效率不高。【参考方案3】:

在 Python 中,所有对象都分配在堆上。 但是 Python 使用了一个特殊的内存分配器,所以 malloc 不会在你每次需要新对象时被调用。 对于缓存的小整数(等)也有一些优化;但是,哪些类型以及如何实现取决于实现。

【讨论】:

【参考方案4】:

如果您想在 Python 中有效地处理数字,请查看 NumPy ( http://numpy.scipy.org/)。它让您可以非常快速地做事,同时仍然可以使用 Python。

要完成你在 NumPy 中的要求,你会做类似的事情

import numpy as np
myarray = np.zeros(4000)

这会给你一个初始化为零的浮点数数组。然后你可以做一些非常酷的事情,比如将整个数组乘以一个因子或其他数组和其他东西(有点像在 Matlab 中,如果你曾经使用过),这非常快(大部分实际工作都发生在NumPy 库的高度优化的 C 部分)。

如果它不是你之后的数字数组,那么你可能不会找到在 Python 中做你想做的事情的方法。 Python 对象列表是内部对象的点列表(无论如何我认为是这样,我不是 Python 内部的专家),因此它仍然会在您创建它们时分配其每个成员。

【讨论】:

正如我在@Mikhail Korobov 的回答中所说,np.empty 更可取,除非你真的需要你的数组从零开始,从而使我的计算机速度提高三倍。【参考方案5】:

这里有四种变体:

增量列表创建 “预分配”列表 array.array() numpy.zeros()

 

python -mtimeit -s"N=10**6" "a = []; app = a.append;"\
    "for i in xrange(N):  app(i);"
10 loops, best of 3: 390 msec per loop

python -mtimeit -s"N=10**6" "a = [None]*N; app = a.append;"\
    "for i in xrange(N):  a[i] = i"
10 loops, best of 3: 245 msec per loop

python -mtimeit -s"from array import array; N=10**6" "a = array('i', [0]*N)"\
    "for i in xrange(N):" "  a[i] = i"
10 loops, best of 3: 541 msec per loop

python -mtimeit -s"from numpy import zeros; N=10**6" "a = zeros(N,dtype='i')"\
    "for i in xrange(N):" "  a[i] = i"
10 loops, best of 3: 353 msec per loop

这表明在这种情况下[None]*N 是最快的,array.array 是最慢的。

【讨论】:

我认为 array.array 在这里的使用不是最理想的,请看我的回答。 @MikhailKorobov:很好的发现。 array('i', [0])*n along 比 array('i', [0]*n) 快 10 倍,但如果添加初始化循环,它仍然比 [0]*n 变体慢。答案的重点:先测量。代码示例来自当时的其他答案。 这似乎对 numpy 和 array 有点不公平,因为您包括了导入时间,这可能会在很多调用中分摊。 @MikhailKorobov 的结果似乎表明,一旦导入 numpy,速度就会快很多。 @MattKrause:import 不包括在内,请注意-s @J.F.Sebastian using a = [0]*n 所有元素都有相同的引用,即如果我执行 a[0] = 1,我将得到 a = [1]*n。我想知道的是分配新内存时结果是否相同?还是我做错了什么?【参考方案6】:

看看这个:

In [7]: %timeit array.array('f', [0.0]*4000*1000)
1 loops, best of 3: 306 ms per loop

In [8]: %timeit array.array('f', [0.0])*4000*1000
100 loops, best of 3: 5.96 ms per loop

In [11]: %timeit np.zeros(4000*1000, dtype='f')
100 loops, best of 3: 6.04 ms per loop

In [9]: %timeit [0.0]*4000*1000
10 loops, best of 3: 32.4 ms per loop

所以永远不要使用array.array('f', [0.0]*N),使用array.array('f', [0.0])*Nnumpy.zeros

【讨论】:

如果您要设置数组元素而不是添加它们,您可能不需要零,只需要为每个元素保留一些空间。在这种情况下,要走的路是np.empty 代替np.zeros。通过您的测试,在我的计算机上速度提高了三倍。【参考方案7】:

对于 Python3:

import timeit
from numpy import zeros
from array import array

def func1():
    N=10**6
    a = []
    app = a.append
    for i in range(N):
        app(i)

def func2():
    N=10**6
    a = [None]*N
    app = a.append
    for i in range(N):
        a[i] = i

def func3():
    N=10**6
    a = array('i', [0]*N)
    for i in range(N):
        a[i] = i

def func4():
    N=10**6
    a = zeros(N,dtype='i')
    for i in range(N):
        a[i] = i

start_time = timeit.default_timer()
func1()
print(timeit.default_timer() - start_time)

start_time = timeit.default_timer()
func2()
print(timeit.default_timer() - start_time)

start_time = timeit.default_timer()
func3()
print(timeit.default_timer() - start_time)

start_time = timeit.default_timer()
func4()
print(timeit.default_timer() - start_time)

结果:

0.1655518
0.10920069999999998
0.1935983
0.15213890000000002
    追加() [无]*N 使用模块数组 使用模块 numpy

【讨论】:

以上是关于为Python中的列表保留内存?的主要内容,如果未能解决你的问题,请参考以下文章

使用xlwings在excel中的函数调用之间保留python全局变量

LazyColumn 项目从列表中删除后保留在内存中

python如何删除列表中的元素

python替换列表中的项目

python列表中的内存泄漏问题

Python 中的内存消耗 - 列表、下标和指针