我有一本嵌套的字典。是否只有一种方法可以安全地传递价值观?
try:
example_dict['key1']['key2']
except KeyError:
pass
或者python有一个类似get()的方法用于嵌套字典?
我有一本嵌套的字典。是否只有一种方法可以安全地传递价值观?
try:
example_dict['key1']['key2']
except KeyError:
pass
或者python有一个类似get()的方法用于嵌套字典?
当前回答
Glom是一个很好的库,可以进入点查询:
In [1]: from glom import glom
In [2]: data = {'a': {'b': {'c': 'd'}}}
In [3]: glom(data, "a.b.c")
Out[3]: 'd'
查询失败有一个很好的堆栈跟踪,指出确切的故障点:
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')
默认保护:
In [5]: glom(data, "a.b.foo", default="spam")
Out[5]: 'spam'
格洛姆的魅力在于多功能的规格参数。例如,可以很容易地从以下数据中提取所有的名字:
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']
更多的例子请阅读glom文档。
其他回答
一个简单的类,可以包装字典,并根据键进行检索:
class FindKey(dict):
def get(self, path, default=None):
keys = path.split(".")
val = None
for key in keys:
if val:
if isinstance(val, list):
val = [v.get(key, default) if v else None for v in val]
else:
val = val.get(key, default)
else:
val = dict.get(self, key, default)
if not val:
break
return val
例如:
person = {'person':{'name':{'first':'John'}}}
FindDict(person).get('person.name.first') # == 'John'
如果该键不存在,则默认返回None。你可以在FindDict包装器中使用default=键覆盖它,例如':
FindDict(person, default='').get('person.name.last') # == doesn't exist, so ''
在深入获取属性后,我使用点表示法安全地获得嵌套的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)
已经有很多很好的答案,但我已经提出了一个类似于JavaScript领域的lodash get的函数,它也支持通过索引进入列表:
def get(value, keys, default_value = None):
'''
Useful for reaching into nested JSON like data
Inspired by JavaScript lodash get and Clojure get-in etc.
'''
if value is None or keys is None:
return None
path = keys.split('.') if isinstance(keys, str) else keys
result = value
def valid_index(key):
return re.match('^([1-9][0-9]*|[0-9])$', key) and int(key) >= 0
def is_dict_like(v):
return hasattr(v, '__getitem__') and hasattr(v, '__contains__')
for key in path:
if isinstance(result, list) and valid_index(key) and int(key) < len(result):
result = result[int(key)] if int(key) < len(result) else None
elif is_dict_like(result) and key in result:
result = result[key]
else:
result = default_value
break
return result
def test_get():
assert get(None, ['foo']) == None
assert get({'foo': 1}, None) == None
assert get(None, None) == None
assert get({'foo': 1}, []) == {'foo': 1}
assert get({'foo': 1}, ['foo']) == 1
assert get({'foo': 1}, ['bar']) == None
assert get({'foo': 1}, ['bar'], 'the default') == 'the default'
assert get({'foo': {'bar': 'hello'}}, ['foo', 'bar']) == 'hello'
assert get({'foo': {'bar': 'hello'}}, 'foo.bar') == 'hello'
assert get({'foo': [{'bar': 'hello'}]}, 'foo.0.bar') == 'hello'
assert get({'foo': [{'bar': 'hello'}]}, 'foo.1') == None
assert get({'foo': [{'bar': 'hello'}]}, 'foo.1.bar') == None
assert get(['foo', 'bar'], '1') == 'bar'
assert get(['foo', 'bar'], '2') == None
我改编了GenesRus和unutbu的答案,非常简单:
class new_dict(dict):
def deep_get(self, *args, default=None):
_empty_dict = {}
out = self
for key in args:
out = out.get(key, _empty_dict)
return out if out else default
它适用于:
d = new_dict(some_data)
d.deep_get("key1", "key2", "key3", ..., default=some_value)
因为如果缺少一个键就会引发一个键错误是合理的,我们甚至可以不检查它,让它像这样单一:
def get_dict(d, kl):
cur = d[kl[0]]
return get_dict(cur, kl[1:]) if len(kl) > 1 else cur