4

我在 SO 上遇到过至少两种体面的嵌套字典实现,一种是使用 defaultdict,另一种是子类化 dict

这两种方法都适用于大多数功能,除了它们在访问不存在的键值对时都有副作用:它为该键创建一个空字典,存储并返回它。

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

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

4

3 回答 3

2

您指向的两个实现对普通s 所做的唯一一件事就是在访问不存在的密钥时dict返回 a 。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 首先必须找到第一个键并将其解析为 adict才能查找第二个键:

>>> example['foo']['bar']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is unsubscriptable
于 2012-05-28T18:19:43.907 回答
2

d[key][subkey] = value如果您希望使用丢失的键,您必须放弃返回 None 的要求。如果d[key] is None,d[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 以与常规 dict 保持一致。如果您愿意,您可以轻松地将其更改为返回 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}}}
于 2012-05-28T18:53:10.510 回答
1

对于这种情况,Python 支持鸭子类型:

>>> 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

将它放入一个类或一个函数中,它应该很容易支持我认为你正在寻找的东西。

于 2012-05-28T23:27:45.130 回答