Python中类似PERL的自动生成默认值,如何从不存在的任意嵌套返回默认值?
假设我想在Python中实现类似PERL的自动创建嵌套结构的功能,也就是说:
>>> d = Autovivifier()
>>> d = ['nested']['key']['value']=10
>>> d
{'nested': {'key': {'value': 10}}}
有几种主要的方法可以做到这一点:
好的,这很简单。
现在假设我想从一个缺少键的字典中返回一个默认值。再次,有几种方法可以做到这一点:
- 对于非嵌套路径,你可以使用
__missing__
钩子 - 使用try/except块来包裹可能缺失的键路径的访问
- 使用
{}.get(key, default)
(这在嵌套字典中不太好用),也就是说,没有autoviv.get(['nested']['key']['no key of this value'], default)
这样的用法
这两个目标似乎存在不可调和的冲突(根据我过去几个小时的尝试)。
这里是问题:
假设我想要一个自动创建嵌套结构的字典,它能 1) 为d['arbitrary']['nested']['path']
创建嵌套结构;并且 2) 在不存在的任意嵌套中返回一个默认值,而不需要用try/except包裹?
这里有几个问题:
- 调用
d['nested']['key']['no key of this value']
等同于(d['nested'])['key']['no key of this value']
。重写__getitem__
不行,因为必须返回一个也重写了__getitem__
的对象。 - 创建自动创建器的方法会在你测试路径是否存在时创建字典条目。也就是说,我不想在用
if d['p1']['sp2']['etc.']
测试时创建整个路径。
我该如何在Python中提供一个字典,使其能够:
- 创建类型为
d['p1']['p2'][etc]=val
的访问路径(自动创建); - 如果你测试存在性,则不创建相同的路径;
- 返回一个默认值(像
{}.get(key, default)
)而不需要用try/except包裹; - 我不需要完整的字典操作。实际上只需要
d=['nested']['key']['value']=val
和d['nested']['key']['no key of this value']
等于一个默认值。我希望测试d['nested']['key']['no key of this value']
时不创建它,但如果创建也可以接受。
?
3 个回答
虽然这并不完全符合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'),)
你可以在空白处填上内容,看看如何实现你想要的效果。
别这么做。其实有更简单的方法,只需要写一个包含你想要的操作的类就行了。即使在Perl中,这也不是一个大家都认可的特性。
不过,确实可以做到,方法是用一个自定义的自动生成类。你需要一个 __getitem__
方法,它返回一个空的自动生成字典,但并不存储它。这个新的自动生成字典会记住创建它的那个字典和键,然后只有在里面存储了一个“真实”的值时,它才会插入到它的父级中。
因为一个空字典在判断时会被视为“假”,所以你可以用Perl的方式来检查它是否存在,而不需要实际创建那些中间的字典。
不过我不打算写出具体的代码,因为我觉得这个主意实在太糟糕了。
要创建一个递归的字典树,可以使用 defaultdict
这个工具,配合一个小技巧:
from collections import defaultdict
tree = lambda: defaultdict(tree)
然后你可以通过 x = tree()
来创建你的 x。
以上内容来自 @BrenBarn -- defaultdict 的嵌套用法