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

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

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


当前回答

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

其他回答

你可以使用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')

我的实现下降到子字典,忽略None值,但失败与TypeError如果发现任何其他

def deep_get(d: dict, *keys, default=None):
    """ Safely get a nested value from a dict

    Example:
        config = {'device': None}
        deep_get(config, 'device', 'settings', 'light')
        # -> None
        
    Example:
        config = {'device': True}
        deep_get(config, 'device', 'settings', 'light')
        # -> TypeError

    Example:
        config = {'device': {'settings': {'light': 'bright'}}}
        deep_get(config, 'device', 'settings', 'light')
        # -> 'light'

    Note that it returns `default` is a key is missing or when it's None.
    It will raise a TypeError if a value is anything else but a dict or None.
    
    Args:
        d: The dict to descend into
        keys: A sequence of keys to follow
        default: Custom default value
    """
    # Descend while we can
    try:
        for k in keys:
            d = d[k]
    # If at any step a key is missing, return default
    except KeyError:
        return default
    # If at any step the value is not a dict...
    except TypeError:
        # ... if it's a None, return default. Assume it would be a dict.
        if d is None:
            return default
        # ... if it's something else, raise
        else:
            raise
    # If the value was found, return it
    else:
        return d

递归法(мб п п и и одитс)

示例dict类型:

foo = [{'feature_name': 'Sample Creator > Contract Details > Elements of the page',
  'scenarios': [{'scenario_name': 'SC, CD, Elements of the page',
                 'scenario_status': 'failed',
                 'scenario_tags': None,
                 'steps': [{'duration': 0,
                            'name': 'I open application Stage and login by '
                                    'SPT_LOGIN and password SPT_PWD',
                            'status': 'untested'},
                           {'duration': 0,
                            'name': 'I open Sample Creator query page',
                            'status': 'untested'},
                           {'duration': 7.78166389465332,
                            'name': 'I open application Stage and login by '
                                    'SPT_LOGIN and password SPT_PWD',
                            'status': 'passed'},
                           {'duration': 3.985326051712036,
                            'name': 'I open Sample Creator query page',
                            'status': 'passed'},
                           {'duration': 2.9063704013824463,
                            'name': 'Enter value: '
                                    'X-2008-CON-007,X-2011-CON-016 in '
                                    'textarea: project_text_area sleep: 1',
                            'status': 'passed'},
                           {'duration': 4.4447715282440186,
                            'name': 'I press on GET DATA',
                            'status': 'passed'},
                           {'duration': 1.1209557056427002,
                            'name': 'Verify the top table on Contract Details',
                            'status': 'passed'},
                           {'duration': 3.8173601627349854,
                            'name': 'I export contract_details table by offset '
                                    'x:100, y:150',
                            'status': 'passed'},
                           {'duration': 1.032956600189209,
                            'name': 'Check data of '
                                    'sc__cd_elements_of_the_page_1 and skip '
                                    'cols None',
                            'status': 'passed'},
                           {'duration': 0.04593634605407715,
                            'name': "Verify 'Number of Substances' column "
                                    'values',
                            'status': 'passed'},
                           {'duration': 0.10199904441833496,
                            'name': 'Substance Sample Details bottom table '
                                    'columns',
                            'status': 'passed'},
                           {'duration': 0.0009999275207519531,
                            'name': 'Verify the Substance Sample Details '
                                    'bottom table',
                            'status': 'passed'},
                           {'duration': 3.8558616638183594,
                            'name': 'I export substance_sample_details table '
                                    'by offset x:100, y:150',
                            'status': 'passed'},
                           {'duration': 1.0329277515411377,
                            'name': 'Check data of '
                                    'sc__cd_elements_of_the_page_2 and skip '
                                    'cols None',
                            'status': 'passed'},
                           {'duration': 0.2879970073699951,
                            'name': 'Click on AG-13369',
                            'status': 'passed'},
                           {'duration': 3.800830364227295,
                            'name': 'I export substance_sample_details table '
                                    'by offset x:100, y:150',
                            'status': 'passed'},
                           {'duration': 1.0169551372528076,
                            'name': 'Check data of '
                                    'sc__cd_elements_of_the_page_3 and skip '
                                    'cols None',
                            'status': 'passed'},
                           {'duration': 1.7484464645385742,
                            'name': 'Select all cells, table: 2',
                            'status': 'passed'},
                           {'duration': 3.812828779220581,
                            'name': 'I export substance_sample_details table '
                                    'by offset x:100, y:150',
                            'status': 'passed'},
                           {'duration': 1.0029594898223877,
                            'name': 'Check data of '
                                    'sc__cd_elements_of_the_page_2 and skip '
                                    'cols None',
                            'status': 'passed'},
                           {'duration': 1.6729373931884766,
                            'name': 'Set window size x:800, y:600',
                            'status': 'passed'},
                           {'duration': 30.145705699920654,
                            'name': 'All scrollers are placed on top 6 and far '
                                    'left 8',
                            'status': 'failed'}]}]},
  {'feature_name': 'Sample Creator > Substance Sample History > Elements of the '
                  'page',
  'scenarios': [{'scenario_name': 'SC, SSH, Elements of the page',
                 'scenario_status': 'passed',
                 'scenario_tags': None,
                 'steps': [{'duration': 0,
                            'name': 'I open application Stage and login by '
                                    'SPT_LOGIN and password SPT_PWD',
                            'status': 'untested'},
                           {'duration': 0,
                            'name': 'I open Sample Creator query page',
                            'status': 'untested'},
                           {'duration': 7.305850505828857,
                            'name': 'I open application Stage and login by '
                                    'SPT_LOGIN and password SPT_PWD',
                            'status': 'passed'},
                           {'duration': 3.500955104827881,
                            'name': 'I open Sample Creator query page',
                            'status': 'passed'},
                           {'duration': 3.0419492721557617,
                            'name': 'Enter value: NOA401800 SYN-NOA '
                                    'A,S4A482070C SYN-ISN-OLD '
                                    'O,S04A482167T,S04A482190Y,CSAA796564,CSCD106701 '
                                    'in textarea: id_text_area sleep: 1',
                            'status': 'passed'},
                           {'duration': 49.567158460617065,
                            'name': 'I press on GET DATA',
                            'status': 'passed'},
                           {'duration': 0.13904356956481934,
                            'name': 'Open substance_sample_history',
                            'status': 'passed'},
                           {'duration': 1.1039845943450928,
                            'name': 'Columns displayed',
                            'status': 'passed'},
                           {'duration': 3.881945848464966,
                            'name': 'I export export_parent_table table by '
                                    'offset x:100, y:150',
                            'status': 'passed'},
                           {'duration': 1.0334820747375488,
                            'name': 'Check data of '
                                    'sc__ssh_elements_of_the_page_1 and skip '
                                    'cols None',
                            'status': 'passed'},
                           {'duration': 0.0319981575012207,
                            'name': "Title is 'Additional Details for Marked "
                                    "Rows'",
                            'status': 'passed'},
                           {'duration': 0.08897256851196289,
                            'name': 'Columns displayed (the same as in top '
                                    'table)',
                            'status': 'passed'},
                           {'duration': 25.192569971084595,
                            'name': 'Verify the content of the bottom table',
                            'status': 'passed'},
                           {'duration': 4.308935880661011,
                            'name': 'I export '
                                    'additional_details_for_marked_rows table '
                                    'by offset x:100, y:150',
                            'status': 'passed'},
                           {'duration': 1.0089836120605469,
                            'name': 'Check data of '
                                    'sc__ssh_elements_of_the_page_1 and skip '
                                    'cols None',
                            'status': 'passed'}]}]}]

代码:

def get_keys(_dict: dict, prefix: list):
    prefix += list(_dict.keys())
    return prefix


def _loop_elements(elems:list, prefix=None, limit=None):
    prefix = prefix or []
    limit = limit or 9
    try:
        if len(elems) != 0 and isinstance(elems, list):
            for _ in elems:
                if isinstance(_, dict):
                    get_keys(_, prefix)
                    for item in _.values():
                        _loop_elements(item, prefix, limit)
        return prefix[:limit]
    except TypeError:
        return


>>>goo = _loop_elements(foo,limit=9)
>>>goo
['feature_name', 'scenarios', 'scenario_name', 'scenario_status', 'scenario_tags', 'steps', 'duration', 'name', 'status']

对于嵌套的字典/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

你也可以使用python reduce:

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