没有副作用嵌套字典实现

时间:2012-05-28 18:06:37

标签: python dictionary

我在SO上遇到过至少两个体面的嵌套字典实现,一个是to use defaultdict,另一个是to subclass dict

这两种方法对于大多数功能都可以正常工作,除了它们在访问不存在的键值对时都有副作用:它为该键创建空字典,存储和退货。

理想情况下,我希望在尝试访问不存在的密钥时,在不创建条目(例如空字典)的情况下返回None的实现。这可行吗?

P.S。我知道我们可以通过使用元组作为键来避免嵌套字典,但是这个实现不起作用,因为我需要访问嵌套字典的每个级别的条目集合。

3 个答案:

答案 0 :(得分:2)

这两项实现,指向普通dict的实现是在访问不存在的密钥时返回dict。您想再次恢复它,从而再次使用默认的dict类型:

>>> example = {}
>>> example['foo']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'foo'
>>> example['foo'] = {}
>>> example['foo']['bar'] = 1

如果您想要返回None而不是dict,那么只需使用defaultdict(lambda: None)代替:

>>> from collections import defaultdict
>>> example = defaultdict(lambda: None)
>>> example['foo'] is None
True

请注意,你不能双管齐下; Python首先必须找到第一个密钥并将其解析为dict,然后才能查找第二个密钥:

>>> example['foo']['bar']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is unsubscriptable

答案 1 :(得分:2)

如果您希望d[key][subkey] = value使用缺少的密钥,则必须放弃返回无的要求。如果d[key] is Noned[key][subkey] = value等同于None[subkey] = value,则无效。

使用缺失值可以做的是返回一个空的类似dict的对象,而不是将它分配给一个键。如果该对象持有对父对象的引用,则可以延迟赋值,直到在层次结构中有明确的赋值。

一个示例实现(这是不完整的,你必须做的不仅仅是覆盖setitem以拥有一个功能齐全的dict子类):

class NestedDict(dict):
    def __init__(self, parent=None, parentkey=None):
        self.parent = parent
        self.parentkey = parentkey

    def __missing__(self, key):
        return NestedDict(self, key)

    def __setitem__(self, key, value):
        if self.parent is not None:
            self.parent[self.parentkey] = self
            self.parent = None
        super(NestedDict, self).__setitem__(key, value)

>>> d = NestedDict()
>>> d[1][2][3] = 4
>>> d[2]
{}
>>> d.keys()
[1]
>>> d[1][2][3] 
4

当密钥是元组时,另一种方法是覆盖__getitem____setitem__来执行嵌套查找。此版本为__getitem__提供了一个KeyError,用于丢失密钥,以便与常规字典保持一致。如果您愿意,可以轻松更改它以返回None。

class NestedDict(dict):
    def __getitem__(self, key):
        if isinstance(key, tuple):
            try:
                x = self
                for k in key:
                    x = x[k]
                return x
            except (KeyError, TypeError):
                raise KeyError(key)
        else:
            return super(NestedDict, self).__getitem__(key)

    def __setitem__(self, key, value):
        if isinstance(key, tuple):
            d = self
            for k in key[:-1]:
                d = d.setdefault(k, NestedDict())
            d[key[-1]] = value
        else:
            super(NestedDict, self).__setitem__(key, value)

>>> d = NestedDict()
>>> d[1,2,3] = 4
>>> d[1,2,3]
4
>>> d[1,2,4]
KeyError: (1, 2, 4)
>>> d
{1: {2: {3: 4}}}

答案 2 :(得分:1)

Python支持duck typing用于此类情况:

>>> d={}
>>> d[1]='Some'
>>> try:
...    att=d[1]
... except KeyError:
...    att=None
... 
>>> print att
Some
>>> try:
...    att=d[1][2][3]
... except KeyError:
...    att=None
... 
>>> print att
None

将其转换为类或函数,它应该可以轻松支持我认为您正在寻找的内容。