使用python来减少使用python对象的内存使用量

问题描述 投票:0回答:1

我正在寻找一些关于如何减少python的内存使用量的技巧。我使用这段代码作为保存数据的主要结构:

http://stevehanov.ca/blog/index.php?id=114

我需要它用于使用烧瓶服务器进行邻近字匹配。我需要放置超过2000万个不同的字符串(它会增加)。现在,当我试图在Trie中投入大约1400万时,我得到了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优化,有没有办法让“letter”对象不那么大以节省一些内存?

请注意,我的困难来自于这封信不仅是[a-z]而且还需要处理所有“unicode范围”(比如强调的字符,但不仅仅是)。顺便说一句,它是一个单一的字符,所以它应该是非常轻的记忆指纹。我怎样才能使用代码点而不是字符串对象(它会更节省内存)?

编辑:在@ juanpa-arrivillaga的回复后添加一些其他信息

所以,首先我看到使用插槽构造没有区别,在我的计算机上,有或没有__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

所以我不明白,为什么。我哪里错了?

这里是我尝试的其他东西,使用“intern”关键字,因为这个值是一个处理“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系列。

我想知道是否有任何来自库的固定len数据类型,如numpy可以帮助我保存一些内存,再次作为新的,我不知道在哪里看。顺便说一句“单词”不是真正的“自然语言单词”,而是“任意长度的字符序列”,它们也可以很长。

从你的回复中,我同意避免在每个节点中存储这个单词会很有效,但是你需要查看链接的文章/代码片段。主要目标不是重建这个单词,而是能够使用这个单词进行有效/非常快速的近似字符串匹配,然后获得与每个最接近的匹配相关的“值”,我不确定我理解目标是什么到树的路径。 (没有到达完整的树?),当匹配时我们只需要匹配原始单词,(但此时我的理解可能是错误的)。

所以我需要在某个地方拥有这个巨大的字典,我想在课堂上封装以方便。但从记忆“重量”的角度来看,这可能是太昂贵了吗?

另外我注意到我的内存使用量已经比你的样本少了(我现在还不知道为什么),但是这里是结构中包含的“letter”的示例值。

>>> s = u"\u266f"
>>> ord(s)
9839
>>> sys.getsizeof(s)
28
>>> sys.getsizeof(ord(s))
12
>>> print s
♯
>>> repr(s)
"u'\\u266f'"
python out-of-memory memory-efficient
1个回答
0
投票

低悬的水果:use __slots__ in your node class,否则,每个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
© www.soinside.com 2019 - 2024. All rights reserved.