[英]python: class vs tuple huge memory overhead (?)
我在元組/列表中存儲了大量復雜數據,但更喜歡使用小包裝類來使數據結構更容易理解,例如
class Person:
def __init__(self, first, last):
self.first = first
self.last = last
p = Person('foo', 'bar')
print(p.last)
...
會優先於
p = ['foo', 'bar']
print(p[1])
...
然而 ,似乎有一個可怕的內存開銷:
l = [Person('foo', 'bar') for i in range(10000000)]
# ipython now taks 1.7 GB RAM
和
del l
l = [('foo', 'bar') for i in range(10000000)]
# now just 118 MB RAM
為什么? 我有沒有想到的任何明顯的替代解決方案?
謝謝!
(我知道,在這個例子中,'wrapper'類看起來很傻。但是當數據變得更復雜和嵌套時,它會更有用)
正如其他人在他們的答案中所說,你必須生成不同的對象才能進行比較。
那么,讓我們比較一些方法。
tuple
l = [(i, i) for i in range(10000000)]
# memory taken by Python3: 1.0 GB
class Person
class Person:
def __init__(self, first, last):
self.first = first
self.last = last
l = [Person(i, i) for i in range(10000000)]
# memory: 2.0 GB
namedtuple
( tuple
+ __slots__
) from collections import namedtuple
Person = namedtuple('Person', 'first last')
l = [Person(i, i) for i in range(10000000)]
# memory: 1.1 GB
namedtuple
基本上是一個擴展tuple
的類,並為所有命名字段使用__slots__
,但它添加了字段getter和一些其他幫助方法(如果使用verbose=True
調用,則可以看到生成的確切代碼)。
class Person
+ __slots__
class Person:
__slots__ = ['first', 'last']
def __init__(self, first, last):
self.first = first
self.last = last
l = [Person(i, i) for i in range(10000000)]
# memory: 0.9 GB
這是上面的namedtuple
的精簡版。 一個明顯的贏家,甚至比純元組更好。
使用__slots__
減少內存占用(在我的測試中從1.7 GB到625 MB),因為每個實例不再需要持有dict
來存儲屬性。
class Person:
__slots__ = ['first', 'last']
def __init__(self, first, last):
self.first = first
self.last = last
缺點是您不能再在創建實例后向其添加屬性; 該類僅為__slots__
屬性中列出的屬性提供內存。
除了關閉__dict__
和__weakref__
之外,還有另一種方法可以通過關閉對循環垃圾收集的支持來減少對象占用的內存量。 它在庫記錄類中實現 :
$ pip install recordclass
>>> import sys
>>> from recordclass import dataobject, make_dataclass
創建類:
class Person(dataobject):
first:str
last:str
要么
>>> Person = make_dataclass('Person', 'first last')
結果:
>>> print(sys.getsizeof(Person(100,100)))
32
對於基於__slot__
的類,我們有:
class Person:
__slots__ = ['first', 'last']
def __init__(self, first, last):
self.first = first
self.last = last
>>> print(sys.getsizeof(Person(100,100)))
56
結果,可以更節省存儲器。
對於基於數據dataobject
的:
l = [Person(i, i) for i in range(10000000)]
memory size: 681 Mb
對於基於__slots__
的:
l = [Person(i, i) for i in range(10000000)]
memory size: 921 Mb
在第二個示例中,您只創建一個對象,因為元組是常量。
>>> l = [('foo', 'bar') for i in range(10000000)]
>>> id(l[0])
4330463176
>>> id(l[1])
4330463176
類具有開銷,屬性保存在字典中。 因此,命名元組只需要一半的內存。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.