python - 仅在赋值时如何实现嵌套字典的自动生成?

标签 python dictionary autovivification

长话短说
如何在为子键赋值时让 super 键在 Python 字典中自动生成,而不在检查子键时让它们自动生成?

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

my_dict[1][2] = 3

如果不先做类似的事情,将无法按预期可靠地工作

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

现在,可以通过使 my_dict 成为覆盖 __missing__ 的类的实例来设置一种自动生成,例如所示在 https://stackoverflow.com/a/19829714/6670909 .

问题:但是,如果您检查此类嵌套字典中是否存在子键,该解决方案会自动自动生成更高级别的键。这导致了以下不幸:

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

我怎样才能避免这种误导性的结果?顺便说一句,在 Perl 中,我可以通过以下方式获得所需的行为

no autovivification qw/exists/;

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

最佳答案

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

my_dict[1][2] = 3

my_dict[1] 导致对字典的 __getitem__ 调用。在这一点上没有办法知道正在进行分配。只有序列中的最后一个 [] 是一个 __setitem__ 调用,除非 mydict[1] 存在,否则它不会成功,否则,你要分配给什么对象?

所以不要使用自动激活。您可以使用 setdefault() 代替,使用常规 dict

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

现在这不是很漂亮,特别是当你嵌套得更深时,所以你可以编写一个辅助方法:

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__ 调用来引发自动激活。这样,我们从一开始就知道我们正在执行一项任务,并且可以在不依赖自动激活的情况下继续进行。

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__ 接受元组中的键,如下所示:

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),].

关于python - 仅在赋值时如何实现嵌套字典的自动生成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42122577/

相关文章:

python - 如何将具有多个不同长度值的嵌套列表转换为python中的pandas数据框?

arrays - 如何快速检查一个值在嵌套字典中

python - 如何根据用户是 django 中的管理员还是员工来重定向登录网址

python - 在 Pandas 中阅读维基百科表格时数值呈现不当

android - 应用程序中的多个 map Activity

perl - 如何在 Perl 中禁用自动生存?

python - python 检查对象是否是列表列表的代码

python - 绘制时间序列数据,其中每小时变化是不同列中的值

exception - perl6 在自动激活中捕获非致命异常