访问dict密钥时的描述符行为

时间:2014-08-22 15:47:26

标签: python dictionary lazy-loading descriptor

我正在实现某种延迟加载。我有一个字典,每个键的值都是一个空列表。当我访问一个键时,我需要运行一些加载逻辑来用相应的列表填充一些值。

因此,当我创建字典(让其命名为d)时,会创建一些密钥:a:[], b:[]。现在,当我访问字典中的一个键时:d['a']我需要运行一些基本上返回计算列表的逻辑(让我们说[1, 2, 3])并且d dict变为:a: [1, 2, 3], b:[]

我希望我能够很好地解释事情。据我所知,这与Python中的描述符类似,因为您可以将自定义逻辑附加到属性。但我需要的是将自定义逻辑附加到字典的,这肯定不能与描述符一起使用。

有没有办法做到这一点?或者也许我可以使用描述符,但以其他方式?

3 个答案:

答案 0 :(得分:2)

您可以在__getitem__

中简单地实现该逻辑
class LazyDict(dict):
    def __init__(self, *args, **kwargs):
        super(LazyDict, self).__init__(*args, **kwargs)
        # init a set which will keep the already loaded keys
        self._loaded = set()

    def __getitem__(self, item):
        if item in self and item not in self._loaded:
            # do the "lazy" action
            self[item] = some_function(item)
            self._loaded.add(item)
        # return the value
        return super(LazyDict, self).__getitem__(self, item)

some_function将填充各个键。 如果您的卸载密钥始终为空列表,__getitem__也可能如下所示:

    def __getitem__(self, item):
        value = super(LazyDict, self).__getitem__(self, item)
        if not value:
            value = self[item] = some_function(item)
        return value

此版本不需要额外的_loaded设置来跟踪加载的是哪些密钥。

答案 1 :(得分:0)

您可以专门化dict课程。

class MyDict(dict):
    # __init__ here is redundant - it can safely be ommited.
    def __init__(self, *args, **kwargs):
        super(MyDict, self).__init__(*args, **kwargs)

    def __getitem__(self, key):
        print("Getting item")
        return super(MyDict, self).__getitem__(key)


d = MyDict()
d[4] = 8
print(d[4])

打印:

>>> Getting item
>>> 8

使用装饰器的方法

class FunctionRel:
    def __init__(self):

        self.rel = {
            1: self.function_1,
            2: self.function_2,
            3: self.function_3,
        }

    def __call__(self, key):
        return self.rel[key]()

    def function_1(self): return [1,1,1]
    def function_2(self): return [2,2,2]
    def function_3(self): return [3,3,3]


def wrapper(func):
    rel = FunctionRel()
    def black_magic(d, key, **kwargs):
        if key not in d.visited_keys:
            l = rel(key)
            d[key] = l
            d.visited_keys.append(key)
        return l
    return black_magic



class MyDict(dict):
    def __init__(self, *args, **kwargs):
        self.visited_keys = [] # Avoid recirsion in __getitem__ wrapper.
        super(MyDict, self).__init__(*args, **kwargs)

    @wrapper
    def __getitem__(self, key):
        return super(MyDict, self).__getitem__(key)


d = MyDict()
print(d[1])
print(d[2])
print(d[3])

输出:

>>> [1,1,1]
>>> [2,2,2]
>>> [3,3,3]

答案 2 :(得分:-3)

当您将值作为空列表时,如果d [key]为空列表,则可以首次检查,然后使用函数获取适当的值。如果从函数返回的值有可能是空列表,您可能希望使用另一个字典来跟踪到目前为止已访问的键。