Python安全方法获取嵌套字典的值

Art*_*rti 111 python methods dictionary except

我有一个嵌套字典.是否只有一种方法可以安全地获得价值?

try:
    example_dict['key1']['key2']
except KeyError:
    pass
Run Code Online (Sandbox Code Playgroud)

或者python有一个像get()嵌套字典的方法?

unu*_*tbu 215

你可以使用get两次:

example_dict.get('key1', {}).get('key2')
Run Code Online (Sandbox Code Playgroud)

None如果存在key1key2不存在,则返回.

请注意,这仍然可以引发AttributeErrorif example_dict['key1']存在但不是dict(或带有get方法的类似dict的对象).try..except你发布的代码会引发一个TypeError而不是example_dict['key1']可以取消订阅的代码.

另一个区别是try...except第一个丢失键后立即发生短路.get电话链没有.


如果您希望保留语法,example_dict['key1']['key2']但不希望它提出KeyErrors,那么您可以使用Hasher配方:

class Hasher(dict):
    # https://stackoverflow.com/a/3405143/190597
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

example_dict = Hasher()
print(example_dict['key1'])
# {}
print(example_dict['key1']['key2'])
# {}
print(type(example_dict['key1']['key2']))
# <class '__main__.Hasher'>
Run Code Online (Sandbox Code Playgroud)

请注意,当缺少键时,这将返回空Hasher.

既然Hasherdict你的子类,你可以使用Hasher,就像使用a一样dict.所有相同的方法和语法都可用,Hashers只是以不同的方式处理丢失的密钥.

您可以将常规dict转换为Hasher如下所示:

hasher = Hasher(example_dict)
Run Code Online (Sandbox Code Playgroud)

并轻松地将a转换Hasher为常规dict:

regular_dict = dict(hasher)
Run Code Online (Sandbox Code Playgroud)

另一个选择是隐藏辅助函数中的丑陋:

def safeget(dct, *keys):
    for key in keys:
        try:
            dct = dct[key]
        except KeyError:
            return None
    return dct
Run Code Online (Sandbox Code Playgroud)

因此,其余代码可以保持相对可读性:

safeget(example_dict, 'key1', 'key2')
Run Code Online (Sandbox Code Playgroud)

  • 那么,python对于这种情况没有漂亮的解决方案?:( (21认同)
  • `safeget` 方法在很多方面都不是很安全,因为它会覆盖原始字典,这意味着您无法安全地执行 `safeget(dct, 'a', 'b') 或 safeget(dct, 'a') 等操作')`。 (3认同)
  • @KurtBourbaki:`dct = dct [key]`*为*local变量*`dct`重新赋值*.这不会改变原始的dict(因此原始的dict不受'safeget`的影响.)另一方面,如果使用`dct [key] = ...`,那么原来的dict就会被修改.换句话说,在Python中[名称绑定到值](https://nedbatchelder.com/text/names.html).为名称赋值新值不会影响旧值(除非没有对旧值的引用,在这种情况下(在CPython中)它将被垃圾收集.) (3认同)
  • 如果嵌套字典的键存在,但值为 null,则“safeget”方法也会失败。它将在下一次迭代中抛出“TypeError: 'NoneType' object is not subscriptable” (2认同)
  • 从 Python 3.4 开始,您可以使用“with suggest(KeyError):”。请参阅此答案:/sf/answers/3211197601/ (2认同)

小智 50

你也可以使用python reduce:

def deep_get(dictionary, *keys):
    return reduce(lambda d, key: d.get(key) if d else None, keys, dictionary)
Run Code Online (Sandbox Code Playgroud)

  • 对此评论稍作修正:_reduce_ 不再是 Py3 中的内置函数。但我不明白为什么这会让它变得不那么优雅。它确实使它不太适合一句台词,但成为一句台词并不会自动限定或取消某些东西的“优雅”资格。 (6认同)
  • 只是想提一下,functools [不再是Python3内置](https://www.artima.com/weblogs/viewpost.jsp?thread=98196)并且需要从functools导入,这使得这种方法略微减少优雅. (5认同)

oli*_*679 30

在第一阶段,您可以获取一个空字典。

example_dict.get('key1',{}).get('key2')
Run Code Online (Sandbox Code Playgroud)


Yud*_*ira 24

通过在这里结合所有这些答案和我所做的小改动,我认为这个功能将是有用的.它安全,快速,易于维护.

def deep_get(dictionary, keys, default=None):
    return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)
Run Code Online (Sandbox Code Playgroud)

示例:

>>> from functools import reduce
>>> def deep_get(dictionary, keys, default=None):
...     return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)
...
>>> person = {'person':{'name':{'first':'John'}}}
>>> print (deep_get(person, "person.name.first"))
John
>>> print (deep_get(person, "person.name.lastname"))
None
>>> print (deep_get(person, "person.name.lastname", default="No lastname"))
No lastname
>>>
Run Code Online (Sandbox Code Playgroud)

  • @编辑你的个人资料。那么你只需要做一些小的修改,将返回值从“None”更改为“Raise KeyError” (3认同)

Jos*_*ban 14

建立Yoav的答案,更安全的方法:

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


hoe*_*ing 10

glom是一个不错的库,也可以进行点查询:

In [1]: from glom import glom

In [2]: data = {'a': {'b': {'c': 'd'}}}

In [3]: glom(data, "a.b.c")
Out[3]: 'd'
Run Code Online (Sandbox Code Playgroud)

查询失败有一个很好的堆栈跟踪,指示确切的失败点:

In [4]: glom(data, "a.b.foo")
---------------------------------------------------------------------------
PathAccessError                           Traceback (most recent call last)
<ipython-input-4-2a3467493ac4> in <module>
----> 1 glom(data, "a.b.foo")

~/.cache/pypoetry/virtualenvs/neural-knapsack-dE7ihQtM-py3.8/lib/python3.8/site-packages/glom/core.py in glom(target, spec, **kwargs)
   2179 
   2180     if err:
-> 2181         raise err
   2182     return ret
   2183 

PathAccessError: error raised while processing, details below.
 Target-spec trace (most recent last):
 - Target: {'a': {'b': {'c': 'd'}}}
 - Spec: 'a.b.foo'
glom.core.PathAccessError: could not access 'foo', part 2 of Path('a', 'b', 'foo'), got error: KeyError('foo')
Run Code Online (Sandbox Code Playgroud)

保障措施default

In [5]: glom(data, "a.b.foo", default="spam")
Out[5]: 'spam'
Run Code Online (Sandbox Code Playgroud)

其优点glom在于多功能的规格参数。例如,可以轻松地从以下内容中提取所有名字data

In [8]: data = {
   ...:     "people": [
   ...:         {"first_name": "Alice", "last_name": "Adams"},
   ...:         {"first_name": "Bob", "last_name": "Barker"}
   ...:     ]
   ...: }

In [9]: glom(data, ("people", ["first_name"]))
Out[9]: ['Alice', 'Bob']
Run Code Online (Sandbox Code Playgroud)

阅读glom文档以获取更多示例。


San*_*ños 9

您可以使用 pydash:

import pydash as _  #NOTE require `pip install pydash`

_.get(example_dict, 'key1.key2', default='Default')
Run Code Online (Sandbox Code Playgroud)

https://pydash.readthedocs.io/en/latest/api.html

  • 在我看来,这必须是公认的答案! (2认同)

Fab*_*amo 8

我建议你试试python-benedict

它是一个dict提供 keypath 支持等的子类。

安装: pip install python-benedict

from benedict import benedict

example_dict = benedict(example_dict, keypath_separator='.')
Run Code Online (Sandbox Code Playgroud)

现在您可以使用keypath访问嵌套值:

from benedict import benedict

example_dict = benedict(example_dict, keypath_separator='.')
Run Code Online (Sandbox Code Playgroud)

或使用键列表访问嵌套值:

val = example_dict['key1.key2']

# using 'get' method to avoid a possible KeyError:
val = example_dict.get('key1.key2')
Run Code Online (Sandbox Code Playgroud)

它在 GitHub 上经过良好测试和开源:

https://github.com/fabiocaccamo/python-benedict

注意:我是这个项目的作者


Pit*_*kos 7

递归解决方案.它不是最有效的,但我发现它比其他示例更具可读性,并且它不依赖于functools.

def deep_get(d, keys):
    if not keys or d is None:
        return d
    return deep_get(d.get(keys[0]), keys[1:])
Run Code Online (Sandbox Code Playgroud)

d = {'meta': {'status': 'OK', 'status_code': 200}}
deep_get(d, ['meta', 'status_code'])     # => 200
deep_get(d, ['garbage', 'status_code'])  # => None
Run Code Online (Sandbox Code Playgroud)

更精致的版本

def deep_get(d, keys, default=None):
    """
    Example:
        d = {'meta': {'status': 'OK', 'status_code': 200}}
        deep_get(d, ['meta', 'status_code'])          # => 200
        deep_get(d, ['garbage', 'status_code'])       # => None
        deep_get(d, ['meta', 'garbage'], default='-') # => '-'
    """
    assert type(keys) is list
    if d is None:
        return default
    if not keys:
        return d
    return deep_get(d.get(keys[0]), keys[1:], default)
Run Code Online (Sandbox Code Playgroud)


zzz*_*zzz 6

虽然简化方法很简洁,但我认为简单的循环更容易理解.我还包括一个默认参数.

def deep_get(_dict, keys, default=None):
    for key in keys:
        if isinstance(_dict, dict):
            _dict = _dict.get(key, default)
        else:
            return default
    return _dict
Run Code Online (Sandbox Code Playgroud)

作为了解减少单线程如何工作的练习,我做了以下工作.但最终循环方法对我来说似乎更直观.

def deep_get(_dict, keys, default=None):

    def _reducer(d, key):
        if isinstance(d, dict):
            return d.get(key, default)
        return default

    return reduce(_reducer, keys, _dict)
Run Code Online (Sandbox Code Playgroud)

用法

nested = {'a': {'b': {'c': 42}}}

print deep_get(nested, ['a', 'b'])
print deep_get(nested, ['a', 'b', 'z', 'z'], default='missing')
Run Code Online (Sandbox Code Playgroud)


IOD*_*DEV 5

从 Python 3.4 开始,您可以使用它with suppress (KeyError)来访问嵌套的 json 对象,而不必担心 Keyerror

from contextlib import suppress

with suppress(KeyError):
    a1 = json_obj['key1']['key2']['key3']
    a2 = json_obj['key4']['key5']['key6']
    a3 = json_obj['key7']['key8']['key9']
Run Code Online (Sandbox Code Playgroud)

由泰龙提供。看看他的答案以获取更多详细信息:/sf/answers/3211197601/