Python中类似PERL的自动生成默认值,如何从不存在的任意嵌套返回默认值?

4 投票
3 回答
621 浏览
提问于 2025-04-18 11:32

假设我想在Python中实现类似PERL的自动创建嵌套结构的功能,也就是说:

>>> d = Autovivifier()
>>> d = ['nested']['key']['value']=10
>>> d
{'nested': {'key': {'value': 10}}}

有几种主要的方法可以做到这一点:

  1. 使用递归的默认字典
  2. 使用__missing__钩子来返回嵌套结构

好的,这很简单。

现在假设我想从一个缺少键的字典中返回一个默认值。再次,有几种方法可以做到这一点:

  1. 对于非嵌套路径,你可以使用__missing__钩子
  2. 使用try/except块来包裹可能缺失的键路径的访问
  3. 使用{}.get(key, default) (这在嵌套字典中不太好用),也就是说,没有autoviv.get(['nested']['key']['no key of this value'], default)这样的用法

这两个目标似乎存在不可调和的冲突(根据我过去几个小时的尝试)。

这里是问题:

假设我想要一个自动创建嵌套结构的字典,它能 1) 为d['arbitrary']['nested']['path']创建嵌套结构;并且 2) 在不存在的任意嵌套中返回一个默认值,而不需要用try/except包裹?

这里有几个问题:

  1. 调用d['nested']['key']['no key of this value'] 等同于 (d['nested'])['key']['no key of this value']。重写__getitem__不行,因为必须返回一个也重写了__getitem__的对象。
  2. 创建自动创建器的方法会在你测试路径是否存在时创建字典条目。也就是说,我不想在用if d['p1']['sp2']['etc.']测试时创建整个路径。

我该如何在Python中提供一个字典,使其能够:

  1. 创建类型为d['p1']['p2'][etc]=val的访问路径(自动创建);
  2. 如果你测试存在性,则不创建相同的路径;
  3. 返回一个默认值(像{}.get(key, default))而不需要用try/except包裹;
  4. 我不需要完整的字典操作。实际上只需要d=['nested']['key']['value']=vald['nested']['key']['no key of this value']等于一个默认值。我希望测试d['nested']['key']['no key of this value']时不创建它,但如果创建也可以接受。

?

3 个回答

1

虽然这并不完全符合Python中的字典协议,但你可以通过实现自己的自动创建字典来获得不错的效果,这种字典使用了可变的getitem参数。大概是这样的(2.x版本):

class ExampleVivifier(object):
    """ Small example class to show how to use varargs in __getitem__. """

    def __getitem__(self, *args):
        print args

使用示例可能是:

>>> v = ExampleVivifier()
>>> v["nested", "dictionary", "path"]
(('nested', 'dictionary', 'path'),)

你可以在空白处填上内容,看看如何实现你想要的效果。

2

别这么做。其实有更简单的方法,只需要写一个包含你想要的操作的类就行了。即使在Perl中,这也不是一个大家都认可的特性。

不过,确实可以做到,方法是用一个自定义的自动生成类。你需要一个 __getitem__ 方法,它返回一个空的自动生成字典,但并不存储它。这个新的自动生成字典会记住创建它的那个字典和键,然后只有在里面存储了一个“真实”的值时,它才会插入到它的父级中。

因为一个空字典在判断时会被视为“假”,所以你可以用Perl的方式来检查它是否存在,而不需要实际创建那些中间的字典。

不过我不打算写出具体的代码,因为我觉得这个主意实在太糟糕了。

4

要创建一个递归的字典树,可以使用 defaultdict 这个工具,配合一个小技巧:

from collections import defaultdict

tree = lambda: defaultdict(tree)

然后你可以通过 x = tree() 来创建你的 x。

以上内容来自 @BrenBarn -- defaultdict 的嵌套用法

撰写回答