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

Python 访问dict密钥时的描述符行为,python,dictionary,lazy-loading,descriptor,Python,Dictionary,Lazy Loading,Descriptor,我正在实现某种延迟加载。我有一个dict,每个键的值都是一个空列表。当我访问一个键时,我需要运行一些加载逻辑,用一些值填充相应的列表 因此,当我创建字典时,我们将其命名为d,创建了一些键:a:[],b:[]。现在,当我访问字典中的一个键时:d['a'],我需要运行一些逻辑,基本上返回一个计算列表,比如[1,2,3],d dict变成:a:[1,2,3],b:[] 我希望我解释得足够好。据我所知,这类似于Python中的描述符,因为您可以将自定义逻辑附加到属性。但我在这里需要的是将自定义逻辑附加到

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

因此,当我创建字典时,我们将其命名为d,创建了一些键:a:[],b:[]。现在,当我访问字典中的一个键时:d['a'],我需要运行一些逻辑,基本上返回一个计算列表,比如[1,2,3],d dict变成:a:[1,2,3],b:[]

我希望我解释得足够好。据我所知,这类似于Python中的描述符,因为您可以将自定义逻辑附加到属性。但我在这里需要的是将自定义逻辑附加到dict的键上,这显然不适用于描述符


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

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

可以专门化dict类

上面印着:

>>> Getting item
>>> 8
使用装饰器的方法 输出:

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

您只需在_getitem__中实现该逻辑即可:

其中某个_函数是填充各个键的任何函数。 如果卸载的键始终为空列表,则\uuuu getitem\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

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

此版本不需要额外的加载集来跟踪哪些键已加载或未加载。

空列表是创建时的键或dict值?据我所知,您似乎想要这样的东西,列表不能是dict的键。默认情况下,空列表被分配给dict的键,这些键本身是通过某种逻辑计算出来的。@AndreiHorak这就是为什么我要问,你写道:我有一个dict,每个键都是空列表,这是错误的。哦,对不起,现在查看并更正了我的错误。请注意,您的_init__;在这里是多余的-可以安全地使用它。这是我当前的方法,但由于dict的值有很多逻辑需要处理,因此我将该值包装在一个单独的类中。这就是为什么我觉得如果类可以是一个描述符的话,它会工作得很好。那么使用类MyDict的getitem方法的某种方式作为装饰器怎么样?听起来很有趣,你能进一步详细说明你的想法吗?此外,dict的值没有一个包装对象。这些值有几个包装类,每个包装类的行为都略有不同。正如我在@Raydel Miranda的回答中所评论的,我的dict中的值被包装在一个类中,否则这也会是我的解决方案。我不明白这与我的或@Raydel的FWIW解决方案有什么不同。描述符对属性的作用几乎与_getitem对键的作用一样。因此,如果在获取密钥之前需要任何非默认操作,则必须修改u_getitem_u_u这正是我的想法,有太多与dict无关的逻辑,因此我将其移动到单独的类中。因此,当我得到dict的密钥时,将检索我的类的一个对象,在检索时,我需要在该对象中为我的自定义逻辑触发一些操作。访问描述符时,get是如何工作的。你不是真的被迫将代码逐字移动到dict。。。?您甚至可以将某些_函数作为LazyDict的输入参数。使用描述符,代码也必须成为修改后的dict类的一部分……好吧,这似乎是目前为止最好的主意。非常感谢。
>>> [1,1,1]
>>> [2,2,2]
>>> [3,3,3]
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)
    def __getitem__(self, item):
        value = super(LazyDict, self).__getitem__(self, item)
        if not value:
            value = self[item] = some_function(item)
        return value