我有一本嵌套的字典。是否只有一种方法可以安全地传递价值观?

try:
    example_dict['key1']['key2']
except KeyError:
    pass

或者python有一个类似get()的方法用于嵌套字典?


当前回答

unutbu回答的一个改编,我发现在我自己的代码中很有用:

example_dict.setdefaut('key1', {}).get('key2')

如果key1还没有这个键,它会为它生成一个字典条目,以避免出现KeyError。如果您希望像我这样以包含键对的嵌套字典结束,这似乎是最简单的解决方案。

其他回答

根据Yoav的回答,一个更安全的方法是:

def deep_get(dictionary, *keys):
    return reduce(lambda d, key: d.get(key, None) if isinstance(d, dict) else None, keys, dictionary)

我使用的一个解决方案类似于double get,但具有使用if else逻辑避免TypeError的额外能力:

    value = example_dict['key1']['key2'] if example_dict.get('key1') and example_dict['key1'].get('key2') else default_value

然而,字典嵌套越多,这就变得越麻烦。

def safeget(_dct, *_keys):
    if not isinstance(_dct, dict): raise TypeError("Is not instance of dict")
    def foo(dct, *keys):
        if len(keys) == 0: return dct
        elif not isinstance(_dct, dict): return None
        else: return foo(dct.get(keys[0], None), *keys[1:])
    return foo(_dct, *_keys)

assert safeget(dict()) == dict()
assert safeget(dict(), "test") == None
assert safeget(dict([["a", 1],["b", 2]]),"a", "d") == None
assert safeget(dict([["a", 1],["b", 2]]),"a") == 1
assert safeget({"a":{"b":{"c": 2}},"d":1}, "a", "b")["c"] == 2

对于二级键检索,你可以这样做:

key2_value = (example_dict.get('key1') or {}).get('key2')

在深入获取属性后,我使用点表示法安全地获得嵌套的dict值。这适用于我,因为我的字典是反序列化的MongoDB对象,所以我知道键名不包含.s。此外,在我的上下文中,我可以指定一个数据中没有的虚假回退值(None),因此在调用函数时可以避免使用try/except模式。

from functools import reduce # Python 3
def deepgetitem(obj, item, fallback=None):
    """Steps through an item chain to get the ultimate value.

    If ultimate value or path to value does not exist, does not raise
    an exception and instead returns `fallback`.

    >>> d = {'snl_final': {'about': {'_icsd': {'icsd_id': 1}}}}
    >>> deepgetitem(d, 'snl_final.about._icsd.icsd_id')
    1
    >>> deepgetitem(d, 'snl_final.about._sandbox.sbx_id')
    >>>
    """
    def getitem(obj, name):
        try:
            return obj[name]
        except (KeyError, TypeError):
            return fallback
    return reduce(getitem, item.split('.'), obj)