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

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

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


当前回答

你可以使用get两次:

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

如果key1或key2不存在,则返回None。

注意,如果example_dict['key1']存在但不是dict(或具有get方法的类dict对象),仍然可能引发AttributeError。如果example_dict['key1']不可下标,你发布的try..except代码将引发TypeError。

另一个区别是try…除非在第一次丢失钥匙后立即发生短路。get调用链则不然。


如果您希望保留语法example_dict['key1']['key2'],但不希望它引发KeyErrors,那么您可以使用哈希recipe:

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

注意,当缺少一个键时,返回一个空的hash。

因为Hasher是dict的一个子类,你可以像使用dict一样使用Hasher。所有相同的方法和语法都是可用的,哈希器只是以不同的方式对待缺失的键。

你可以像这样把一个普通字典转换成哈希:

hasher = Hasher(example_dict)

并将哈希转换为普通字典一样容易:

regular_dict = dict(hasher)

另一种选择是在helper函数中隐藏丑陋的代码:

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

这样你剩下的代码就可以保持相对的可读性:

safeget(example_dict, 'key1', 'key2')

其他回答

因为如果缺少一个键就会引发一个键错误是合理的,我们甚至可以不检查它,让它像这样单一:

def get_dict(d, kl):
  cur = d[kl[0]]
  return get_dict(cur, kl[1:]) if len(kl) > 1 else cur

你可以使用pydash:

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

_.get(example_dict, 'key1.key2', default='Default')

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

对于嵌套的字典/JSON查找,可以使用dictor

PIP安装指示器

dict对象

{
    "characters": {
        "Lonestar": {
            "id": 55923,
            "role": "renegade",
            "items": [
                "space winnebago",
                "leather jacket"
            ]
        },
        "Barfolomew": {
            "id": 55924,
            "role": "mawg",
            "items": [
                "peanut butter jar",
                "waggy tail"
            ]
        },
        "Dark Helmet": {
            "id": 99999,
            "role": "Good is dumb",
            "items": [
                "Shwartz",
                "helmet"
            ]
        },
        "Skroob": {
            "id": 12345,
            "role": "Spaceballs CEO",
            "items": [
                "luggage"
            ]
        }
    }
}

要获得龙星的物品,只需提供一个点分隔的路径,即

import json
from dictor import dictor

with open('test.json') as data: 
    data = json.load(data)

print dictor(data, 'characters.Lonestar.items')

>> [u'space winnebago', u'leather jacket']

如果键不在路径中,您可以提供回退值

你还有很多选择,比如忽略字母大小写,使用'以外的其他字符。作为路径分隔符,

https://github.com/perfecto25/dictor

如果您想使用另一个库来解决问题,这是最好的方法

https://github.com/maztohir/dict-path

from dict-path import DictPath

data_dict = {
  "foo1": "bar1",
  "foo2": "bar2",
  "foo3": {
     "foo4": "bar4",
     "foo5": {
        "foo6": "bar6",
        "foo7": "bar7",
     },
  }
}

data_dict_path = DictPath(data_dict)
data_dict_path.get('key1/key2/key3')

在深入获取属性后,我使用点表示法安全地获得嵌套的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)