Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/18.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_Python 3.x_Dictionary_Memory_Optimization - Fatal编程技术网

Python 许多字典使用大量内存

Python 许多字典使用大量内存,python,python-3.x,dictionary,memory,optimization,Python,Python 3.x,Dictionary,Memory,Optimization,我有一个非常简单的Python脚本(出于测试目的)在一个列表中创建3500万个字典对象。每个dictionary对象包含两个键/值对。例如 {'Name': 'Jordan', 'Age': 35} 该脚本只需查询姓名和年龄,搜索字典列表并返回一个新列表,其中包含所有匹配字典条目的索引 然而,正如你在下面看到的,大量的内存被消耗掉了。我想我在某个地方犯了一个非常幼稚的错误 我的代码如下:(如果可读性更好,也可以在图像中查看) 导入系统 #首先,我们将在内存中创建3500万条记录,除了一条记录

我有一个非常简单的Python脚本(出于测试目的)在一个列表中创建3500万个字典对象。每个dictionary对象包含两个键/值对。例如

{'Name': 'Jordan', 'Age': 35}
该脚本只需查询姓名和年龄,搜索字典列表并返回一个新列表,其中包含所有匹配字典条目的索引

然而,正如你在下面看到的,大量的内存被消耗掉了。我想我在某个地方犯了一个非常幼稚的错误

我的代码如下:(如果可读性更好,也可以在图像中查看)

导入系统 #首先,我们将在内存中创建3500万条记录,除了一条记录外,所有记录都是一样的 def搜索(键、值、数据、年龄): 打印(“正在搜索,请稍候”) #创建列表以存储返回的PK foundPKS=[] 对于范围(0,len(数据))中的索引: 如果输入数据[索引]并输入数据[索引]中的“年龄”: 如果数据[index][key]==值,数据[index][Age']>=年龄: foundPKS.append(索引) 结果=foundPKS 返回结果 def createdata(): #让我们创建存储词典的列表 打印(“正在创建数据库,请稍候”) dictList=[] 对于范围内的索引(0,35000000): #定义字典 记录={'Name':'Jordan','Age':25} 如果24500123让我们看看这个

>>> import sys 
>>> sys.getsizeof({'Name': 'Jordan', 'Age': 25}) * 35000000
10080000000
所以大约10 GB。Python正在做您要求它做的事情


您需要将其拆分为多个卡盘,并按顺序进行检查。作为起点,尝试dict对象的开销相当大。这取决于您的Python版本和系统架构,但取决于Python 3.5 64位

In [21]: sys.getsizeof({})
Out[21]: 288
所以猜测:

250*36e6*1e-9 == 9.0
因此,如果我创建了那么多字典,而不是在
列表
中进行分解,那么这就是我在gigabytes中ram使用的下限

与其使用dict作为记录类型(这不是真正的用例),不如使用
namedtuple

为了了解这两者的比较情况,让我们建立一个等价的元组列表:

In [23]: Record = namedtuple("Record", "name age")

In [24]: records = [Record("john", 28) for _ in range(36000000)]

In [25]: getsizeof = sys.getsizeof
考虑:

In [31]: sum(getsizeof(record)+ getsizeof(record.name) + getsizeof(record.age)  for record in records)
Out[31]: 5220000000

In [32]: _ + getsizeof(records)
Out[32]: 5517842208

In [33]: _ * 1e-9
Out[33]: 5.517842208
因此,5次演出是一个非常保守的上限。例如,它假设不存在小的int缓存,这对于记录类型的年龄来说是完全重要的。在我自己的系统上,python进程记录了2.7G的内存使用量(通过
top

因此,我的机器中实际发生的事情最好是对字符串进行保守建模,假设唯一字符串的平均大小为10,因此没有字符串插入,但对int来说是自由的,假设int缓存为我们处理
int
对象,所以我们只需要担心8字节指针

In [35]: sum(getsizeof("0123456789") + 8  for record in records)
Out[35]: 2412000000

In [36]: _ + getsizeof(records)
Out[36]: 2709842208

In [37]: _ * 1e-9
Out[37]: 2.709842208
这是我从
top
观察到的一个很好的模型

如果你真的想要高效的存储 现在,如果你真的想把数据塞进ram,你将不得不失去Python的灵活性。您可以将
array
模块与
struct
结合使用,以获得类似C的内存效率。一个更容易涉入的世界可能是
numpy
,它允许类似的事情。例如:

In [1]: import numpy as np

In [2]: recordtype = np.dtype([('name', 'S20'),('age', np.uint8)])

In [3]: records = np.empty((36000000), dtype=recordtype)

In [4]: records.nbytes
Out[4]: 756000000

In [5]: records.nbytes*1e-9
Out[5]: 0.756
注意,我们现在可以非常紧凑。我可以使用8位无符号整数(即单字节)来表示年龄。然而,我马上就面临一些不灵活的问题:如果我想要有效地存储字符串,我必须定义一个最大大小。我使用了20个字符的
'S20'
。这些是ASCII字节,但一个包含20个ASCII字符的字段可能就足以作为名称

现在,
numpy
为您提供了许多包装C编译代码的快速方法。所以,为了玩玩它,让我们用一些玩具数据填充我们的记录。姓名将只是简单计数中的一串数字,年龄将从平均值为50、标准偏差为10的正态分布中选择

In [8]: for i in range(1, 36000000+1):
   ...:     records['name'][i - 1] = b"%08d" % i
   ...:

In [9]: import random
   ...: for i in range(36000000):
   ...:     records['age'][i] = max(0, int(random.normalvariate(50, 10)))
   ...:
现在,我们可以使用numpy查询
记录。例如,如果希望记录的索引满足某些条件,请使用
np。其中

In [10]: np.where(records['age'] > 70)
Out[10]: (array([      58,      146,      192, ..., 35999635, 35999768, 35999927]),)

In [11]: idx = np.where(records['age'] > 70)[0]

In [12]: len(idx)
Out[12]: 643403
因此
643403
记录的年龄
>70
。现在,让我们试试
100

In [13]: idx = np.where(records['age'] > 100)[0]

In [14]: len(idx)
Out[14]: 9

In [15]: idx
Out[15]:
array([ 2315458,  5088296,  5161049,  7079762, 15574072, 17995993,
       25665975, 26724665, 28322943])

In [16]: records[idx]
Out[16]:
array([(b'02315459', 101), (b'05088297', 102), (b'05161050', 101),
       (b'07079763', 104), (b'15574073', 101), (b'17995994', 102),
       (b'25665976', 101), (b'26724666', 102), (b'28322944', 101)],
      dtype=[('name', 'S20'), ('age', 'u1')])
当然,
numpy
数组的大小是一个主要的不灵活因素。调整操作的大小是昂贵的。现在,您可以将
numpy.array
封装到某个类中,它将充当一个有效的主干,但在这一点上,您还可以使用一个真正的数据库。幸运的是,Python附带了
sqlite

。。。列表中有3500万个字典对象。每个字典对象 包含两个键/值对。例如,{'Name':'Jordan','Age':35}

您是对的,这种存储方式有相当大的开销

研究表明,解决方案需要考虑到共同点。这里有两种方法可以更好地利用空间来替代相同数据的存储

您可以使用来节省类实例上的空间(这将禁止创建每个实例的字典):

使用密集的数据结构(如一对平行列表)更节省空间:

s_name = ['Jordan', 'Martin', 'Mary']
s_age = [35, 31, 33]
如果数据中存在重复项,则可以通过以下值节省更多空间:

s_name = map(intern, s_name)
或者在Python 3中:

s_name = list(map(sys.intern, s_name)

是的,不要用3600万条。使用元组或更好的名称元组我假设RAM的使用是由dict所需的开销造成的,因为原始字符串数据只会使250-300MB非常有用。谢谢我确实希望数据保留在内存中以提高性能。一个Dict似乎要消耗相当多的开销。@Jordan-
pandas
可能也是一个不错的选择。它还将使搜索和匹配变得更加容易,因为
pandas
已经构建了该功能。。。或者直接购买moar RAM:)看起来如果我真的想在内存中维护数据,一个命名的双工将成为赢家。非常有用,谢谢。@Jordan我要添加一个comparison@Jordan这是一个有价值的改进
s_name = map(intern, s_name)
s_name = list(map(sys.intern, s_name)