首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >延迟创建python dict,直到设置/更新

延迟创建python dict,直到设置/更新
EN

Stack Overflow用户
提问于 2017-05-10 22:07:29
回答 1查看 223关注 0票数 1

我有一个包含大量外部字典键(数百万到数十亿)的Python字典结构。内部的dict大部分是空的,但可以存储键值对。目前,我创建了一个单独的字典作为每个内部字典。但它使用了大量的内存,而我最终并没有使用它们。每个空字典都很小,但我有很多空字典。我想推迟创建内部字典,直到需要。

理想情况下,我甚至希望延迟创建内部dict,直到内部dict中的键值对为。我设想对所有外部dict值使用单个DelayDict对象。对于get和getitem调用,这个对象的作用就像一个空字典,但是一旦一个setitem或update调用进来,它就会创建一个空字典来代替它。我遇到了让delaydict对象知道如何将新的空字典与dict-of-dict结构连接起来的问题。

代码语言:javascript
复制
class DelayDict(object):    % can do much more - only showing get/set
    def __init__(self, dod):
        self.dictofdict = dod     % the outer dict
    def __getitem__(self, key):
        raise KeyError(key)
    def __setitem__(self, key, value):
        replacement = {key: value}
        % replace myself in the outer dict!!
        self.dict-of-dict[?????] = replacement

我不知道如何在dict- of -dict结构中存储新的替换字典,以便将DelayDict类替换为内部字典。我知道属性可以做类似的事情,但我相信当我试图在外部字典中替换自己时,同样的基本问题也会出现。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-06-02 14:22:43

老问题,但我遇到了类似的问题。我不确定尝试节省一些内存是不是一个好主意,但是如果你真的需要这样做,你应该尝试构建你自己的数据结构。

如果你坚持使用dict,这里有一个解决方案。

首先,您需要一种在OuterDict中创建不带值的键的方法(默认情况下,值为{})。如果OuterDict是dict __d包装器

代码语言:javascript
复制
def create(self, key):
    self.__d[key] = None

你会留出多少内存?

代码语言:javascript
复制
>>> import sys
>>> a = {}
>>> sys.getsizeof(a)
136

正如您所指出的,None只创建一次,但您必须在其上保留一个引用。在Cpython (64位)中,它是8个字节。对于10亿个元素,您可以节省(136-8)* 10**9字节= 128 Gb (不是Mb,谢谢!)。当有人要求提供值时,您需要提供占位符。占位符跟踪外部字典和外部字典中的键。它包装一个字典,并在你赋值时将这个字典赋给outer[key]

别再说话了,代码:

代码语言:javascript
复制
class OuterDict():
    def __init__(self):
        self.__d = {}

    def __getitem__(self, key):
        v = self.__d[key]
        if v is None: # an orphan key
            v = PlaceHolder(self.__d, key)
        return v

    def create(self, key):
        self.__d[key] = None

class PlaceHolder():
    def __init__(self, parent, key):
        self.__parent = parent
        self.__key = key
        self.__d = {}

    def __getitem__(self, key):
        return self.__d[key]

    def __setitem__(self, key, value):
        if not self.__d:
            self.__parent[self.__key] = self.__d  # copy me in the outer dict
        self.__d[key] = value

    def __repr__(self):
        return repr("PlaceHolder for "+str(self.__d))

    # __len__, ...

一个测试:

代码语言:javascript
复制
o = OuterDict()
o.create("a") # a is empty
print (o["a"])

try:
    o["a"]["b"] # Key Error
except KeyError as e:
    print ("KeyError", e)

o["a"]["b"] = 2
print (o["a"])

# output:
# 'PlaceHolder for {}'
# KeyError 'b'
# {'b': 2}

为什么它不占用太多内存?因为您不是在构建数十亿个占位符。当你不再需要它们的时候,你可以释放它们。也许你一次只需要一个。

可能的改进:您可以创建一个PlaceHolders池。堆栈可能是一个很好的数据结构:最近创建的占位符可能很快就会发布。当你需要一个新的PlaceHolder时,你可以查看堆栈,如果一个占位符只有一个ref (sys.getrefcount(ph) == 1),你可以使用它。为了加快这一过程,当您正在寻找空闲占位符时,您可以记住具有最大引用计数的占位符。您可以使用这个"max refcount“占位符来切换空闲占位符。因此,具有最大引用计数的占位符被发送到堆栈的底部。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43894902

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档