分配值时,如何仅对嵌套字典实现自动生存? [英] How to implement autovivification for nested dictionary ONLY when assigning values?

查看:50
本文介绍了分配值时,如何仅对嵌套字典实现自动生存?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TL; DR
在为子键分配值时如何在Python dict中自动保存超级键,而在检查子键时又如何使其自动保存?

背景:通常,在Python中,在嵌套字典中设置值需要手动确保在分配给它们的子键之前存在更高级别的键.就是

Background: Normally in Python, setting values in a nested dictionary requires manually ensuring that higher-level keys exist before assigning to their sub-keys. That is,

my_dict[1][2] = 3

如果不先做类似的事情

if 1 not in my_dict:
    my_dict[1] = {}

现在,可以通过将 my_dict 设置为覆盖 __ missing __ 的类的实例来建立一种自动生存功能,例如,在 https://stackoverflow.com/a/19829714/6670909 中.

Now, it is possible to set up a kind of autovivification by making my_dict an instance of a class that overrides __missing__, as shown e.g. in https://stackoverflow.com/a/19829714/6670909.

问题:但是,如果您检查这种嵌套dict中是否存在子键,则该解决方案会自动使更高级别的键自动生效.这导致以下不幸:

Question: However, that solution silently autovivifies higher-level keys if you check for the existence of a sub-key in such a nested dict. That leads to the following unfortunateness:

>>> vd = Vividict()
>>> 1 in vd
False
>>> 2 in vd[1]
False
>>> 1 in vd
True

如何避免产生误导性的结果?顺便说一下,在Perl中,我可以通过执行操作获得所需的行为

How can I avoid that misleading result? In Perl, by the way, I can get the desired behavior by doing

no autovivification qw/exists/;

基本上,如果可能的话,我想在Python中复制该行为.

And basically I'd like to replicate that behavior in Python if possible.

推荐答案

这不是一个容易解决的问题,因为在您的示例中:

This is not an easy problem to solve, because in your example:

my_dict[1][2] = 3

my_dict [1] 导致对字典的 __ getitem __ 调用.在这一点上没有办法知道正在分配.只有序列中的最后一个 [] __ setitem __ 调用,除非存在 mydict [1] ,否则该调用不会成功,因为否则,您要分配给哪个对象?

my_dict[1] results in a __getitem__ call on the dictionary. There is no way at that point to know that an assignment is being made. Only the last [] in the sequence is a __setitem__ call, and it can't succeed unless mydict[1] exists, because otherwise, what object are you assigning into?

所以不要使用自动生存.您可以改用 setdefault()和常规的 dict .

So don't use autovivication. You can use setdefault() instead, with a regular dict.

my_dict.setdefault(1, {})[2] = 3

现在这还不是很漂亮,尤其是当您嵌套得更深时,因此您可以编写一个辅助方法:

Now that's not exactly pretty, especially when you are nesting more deeply, so you might write a helper method:

class MyDict(dict):
    def nest(self, keys, value):
       for key in keys[:-1]:
          self = self.setdefault(key, {})
       self[keys[-1]] = value

 my_dict = MyDict()
 my_dict.nest((1, 2), 3)       # my_dict[1][2] = 3

但是更好的方法是将其包装到一个新的 __ setitem __ 中,它可以一次获取所有索引,而不是需要引起自生的中间 __ getitem __ 调用.这样一来,我们就从一开始就知道我们正在做作业,并且可以继续进行而无需依赖生存能力.

But even better is to wrap this into a new __setitem__ that takes all the indexes at once, instead of requiring the intermediate __getitem__ calls that induce the autovivication. This way, we know from the beginning that we're doing an assignment and can proceed without relying on autovivication.

class MyDict(dict):
    def __setitem__(self, keys, value):
       if not isinstance(keys, tuple):
           return dict.__setitem__(self, keys, value)
       for key in keys[:-1]:
          self = self.setdefault(key, {})
       dict.__setitem__(self, keys[-1], value)

my_dict = MyDict()
my_dict[1, 2] = 3

为了保持一致,您还可以提供 __ getitem __ ,它接受元组中的键,如下所示:

For consistency, you could also provide __getitem__ that accepts keys in a tuple as follows:

def __getitem__(self, keys):
   if not isinstance(keys, tuple):
       return dict.__getitem__(self, keys)
   for key in keys:
       self = dict.__getitem__(self, key)
   return self

我能想到的唯一缺点是,我们不能轻易地将元组用作字典键:我们必须将其写为例如 my_dict [(1,2),] .

The only downside I can think of is that we can't use tuples as dictionary keys as easily: we have to write that as, e.g. my_dict[(1, 2),].

这篇关于分配值时,如何仅对嵌套字典实现自动生存?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆