我正在寻找一些有关如何减少python内存使用量的提示。我正在使用此代码作为主要结构来保存我的数据:
http://stevehanov.ca/blog/index.php?id=114
我需要使用烧瓶服务器来匹配接近单词。我需要放置超过200万不同的字符串(它将增加(。现在,我在尝试将约1400万人放入Trie时得到了MemoryError。
我只是添加了一个字典来保持一些快速访问的价值(我需要它,但可以将其视为一种外观ID,它与单词无直接相关(
class TrieNode:
values = {}
def __init__(self):
self.word = None
self.children = {}
global NodeCount
NodeCount += 1
def insert( self, word, value):
node = self
for letter in word:
if letter not in node.children:
node.children[letter] = TrieNode()
node = node.children[letter]
TrieNode.values[word] = value
node.word = word
我不熟悉Python优化,是否有任何方法可以使"字母"对象不那么大以保存一些内存?
请注意,我的困难来自以下事实:这封信不仅是[A-Z],而且需要处理所有" Unicode范围"(如加重的字符,而不仅仅是(。顺便说一句,它是a 单个字符,因此从内存指纹中应该很轻。如何使用CODEPOINT代替字符串对象(会更有效地内存(?
编辑:添加其他一些信息后, @juanpa-arrivillaga的答复
所以,首先,我使用 slot 在我的计算机上,有或没有__slot__
的构造没有区别。
使用__slot__
:
>>> class TrieNode:
NodeCount = 0
__slots__ = "word", "children"
def __init__(self):
self.word = None
self.children = {}
#global NodeCount # my goal is to encapsulated the NodeCount in the class itself
TrieNode.NodeCount += 1
>>> tn = TrieNode()
>>> sys.getsizeof(tn) + sys.getsizeof(tn.__dict__)
176
没有__slot__
:
>>> class TrieNode:
NodeCount = 0
def __init__(self):
self.word = None
self.children = {}
#global NodeCount
TrieNode.NodeCount += 1
>>> tn = TrieNode()
>>> sys.getsizeof(tn) + sys.getsizeof(tn.__dict__)
176
所以我不明白,为什么。我在哪里错了?
这也是我使用"实习"关键字的其他方法,因为此值是处理" ID"的字符串(因此与Unicode无关,不喜欢字母(:
顺便说一句,我的目标是拥有价值和nodecount,这是类/静态变量的等效概念,以便每个人都会由小型创建的objets的所有实例共享,我认为它可以保留内存并避免重复,但是我对python中的"类似静态"概念的理解可能是错误的(
class TrieNode:
values = {} # shared amon all instances so only one structure?
NodeCount = 0
__slots__ = "word", "children"
def __init__(self):
self.word = None
self.children = {}
#global NodeCount
TrieNode.NodeCount += 1
def insert( self, word, value = None):
# value is a string id like "XYZ999999999"
node = self
for letter in word:
codepoint = ord(letter)
if codepoint not in node.children:
node.children[codepoint] = TrieNode()
node = node.children[codepoint]
node.word = word
if value is not None:
lost = TrieNode.values.setdefault(word, [])
TrieNode.values[word].append(intern(str(value)))
添加:最后,我应该表明我正在使用Python 2.7.x family。
我想知道是否有库中的Liby中有任何固定的LEN数据类型可以帮助我节省一些内存,再次是新的,我不知道在哪里看。顺便说一句,"单词"不是真实的"自然语言",而是"字符的任意长度顺序",它们也可能很长。
根据您的答复,我同意避免将单词存储在每个节点中是有效的,但是您需要查看链接的文章/代码。主要目标不是重建这个词,而是能够使用此词进行有效/非常快速的字符串匹配,然后获得与每个最接近匹配的"值",我不确定我是否了解目标是什么沿着树的路径。(不到达完整的树吗?(,当匹配时,我们只需要匹配原始单词(但此时我的理解可能是错误的(。
所以我需要在某个地方有这个巨大的命令,我想把班级封装以使其方便。但是从内存的"重量"角度来看,它可能太昂贵了?
我还注意到,我已经比您的样本更少的内存使用量(我现在不知道为什么(,但是这是结构中包含的"字母"的示例值。
>>> s = u"u266f"
>>> ord(s)
9839
>>> sys.getsizeof(s)
28
>>> sys.getsizeof(ord(s))
12
>>> print s
♯
>>> repr(s)
"u'\u266f'"
低悬挂水果:在您的节点类中使用__slots__
,否则,每个TrieNode
对象都围绕dict
。
class TrieNode:
__slots__ = "word", "children"
def __init__(self):
self.word = None
self.children = {}
现在,每个Trienode对象都不会携带属性dict
。比较大小:
>>> class TrieNode:
... def __init__(self):
... self.word = None
... self.children = {}
...
>>> tn = TrieNode()
>>> sys.getsizeof(tn) + sys.getsizeof(tn.__dict__)
168
vs:
>>> class TrieNode:
... __slots__ = "word", "children"
... def __init__(self):
... self.is_word = False
... self.children = {}
...
>>> sys.getsizeof(tn)
56
>>> tn.__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'TrieNode' object has no attribute '__dict__'
另一种优化,使用int
对象。小int
对象被缓存,可能大多数字符都在该范围内,但是即使不是,int
虽然在Python中仍然耐磨,但甚至比单个字符字符串小:
>>> 'ñ'
'ñ'
>>> ord('ñ')
241
>>> sys.getsizeof('ñ')
74
>>> sys.getsizeof(ord('ñ'))
28
因此,您可以做类似:
的事情def insert( self, word, value):
node = self
for letter in word:
code_point = ord(letter)
if code_point not in node.children:
node.children[code_point] = TrieNode()
node = node.children[code_point]
node.is_word = True #Don't save the word, simply a reference to a singleton
另外,您正在围绕一个巨大增长的类变量values
dict
,但该信息是多余的。你说:
我只是添加一个字典来保持一些价值,并可以快速访问(我需要 它(
您可以从路径中重建单词。它应该相对较快,我会认真考虑不要使用此dict
。查看仅容纳一百万单字符串:
>>> d = {str(i):i for i in range(1000000)}
>>> (sum(sizeof(k)+sizeof(v) for k,v in d.items()) + sizeof(d)) * 1e-9
0.12483203000000001
您可以做类似的事情:
class TrieNode:
__slots__ = "value", "children"
def __init__(self):
self.value = None
self.children = {}
def insert( self, word, value):
node = self
for letter in word:
code_point = ord(letter)
if code_point not in node.children:
node.children[code_point] = TrieNode()
node = node.children[code_point]
node.value = value #this serves as a signal that it is a word
def get(word, default=None):
val = self._get_value(word)
if val is None:
return default
else:
return val
def _get_value(self, word):
node = self
for letter in word:
code_point = ord(letter)
try:
node = node.children[code_point]
except KeyError:
return None
return node.value