【问题标题】:Python Chained get() Method With List Element inside JSONPython 链式 get() 方法与 JSON 中的列表元素
【发布时间】:2017-12-18 01:05:33
【问题描述】:

[Python 2.7]

我有一个 JSON 源,它并不总是返回预期键的完整列表。我正在使用链式gets() 来解决这个问题。

d = {'a': {'b': 1}}

print(d.get('a', {}).get('b', 'NA'))
print(d.get('a', {}).get('c', 'NA'))

>>> 1
>>> NA

但是,有些字典在列表中:

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

print(d['a']['b'][0]['c'])

>>> 2

我不能使用 get() 方法来解决这个问题,因为列表不支持 get() 属性:

d.get('a', {}).get('b', []).get('c', 'NA')

>>> AttributeError: 'list' object has no attribute 'get'

除了捕获数百个潜在的 KeyErrors 之外,是否有更好的方法来解释潜在的缺失 ['c'](与上面的链式 get() 构造类似)?

【问题讨论】:

  • .get('b', [{}])[0],也许吧?
  • “除了捕获数百个潜在的 KeyErrors,[...]” try: ... except: 有什么问题?
  • @jonrsharpe:谢谢。这就是我一直在寻找的。请考虑将其添加为问题的答案。
  • @Rawing:在 OP 中回答。我想避免使用数百个 try 块。我尝试了一次递归,但考虑到源代码的复杂性,它变得太笨拙了。
  • 我不明白您为什么需要多个 try 块。编写一个函数来检索您想要的 dict/list 项,如果抛出异常,则返回一个默认值。

标签: python json python-2.7 list dictionary


【解决方案1】:

我同意@stovfl 的观点,即编写自己的查找函数是可行的方法。虽然,我不认为递归实现是必要的。以下应该足够好:

def nested_lookup(obj, keys, default='NA'):
    current = obj
    for key in keys:
        current = current if isinstance(current, list) else [current]
        try:
            current = next(sub[key] for sub in current if key in sub)
        except StopIteration:
            return default
    return current


d = {'a': {'b': [{'c': 2}, {'d': 3}]}}

print nested_lookup(d, ('a', 'b', 'c'))  # 2
print nested_lookup(d, ('a', 'b', 'd'))  # 3
print nested_lookup(d, ('a', 'c'))       # NA

类方法似乎不太好,因为您将创建许多不必要的对象,并且如果您曾经尝试查找不是叶子的节点,那么您将结束使用自定义对象而不是实际的节点对象。

【讨论】:

  • 谢谢你。它运作良好,看起来很简单。即使@stovfl 的第二个示例也运行良好,我也会接受这个答案。我自己无法提出令人满意的功能,因此无法实施 Rawing 提出的将其移至功能的建议。
【解决方案2】:

问题:我不能使用 get() ... 因为列表不支持 get()。

  1. 您可以实现自己的list.get(),例如:

    class myGET(object):
        def __init__(self, data):
            if isinstance(data, dict):
                self.__dict__.update(data)
            if isinstance(data, list):
                for d in data:
                    self.__dict__.update(d)
    
        def get(self, key, default=None):
            if hasattr(self, key):
                _attr = object.__getattribute__(self, key)
                if isinstance(_attr, (list, dict)):
                    return myGET(_attr)
                else:
                    return _attr
            else:
                return default
    
    d = {'a': {'b': [{'c': 2}]}}
    myGET(d).get('a', {}).get('b', []).get('c', 'NA')
    >>> 2
    
    myGET(d).get('a', {}).get('b', []).get('d', 'NA')
    >>> NA
    
  2. 一个递归的解决方案,不需要链接,例如:

    def get(_dict, subkey, default):
        def _get(_dict, key, deep):
            if key == subkey[deep]:
                if deep == len(subkey) - 1:
                    return _dict
                else:
                    return _get(_dict, None, deep + 1)
    
            elif isinstance(_dict, dict):
                for k in _dict:
                    match = _get(_dict[k], k, deep)
                    if match: return match
    
            elif isinstance(_dict, list):
                for e in _dict:
                    match = _get(e, None, deep)
                    if match: return match
    
        if not isinstance(subkey, (tuple, list)):
            subkey = (subkey)
    
        _r = _get(_dict, None, 0)
        if not _r: return default
        else:      return _r
    
    get(d, 'c', 'NA')
    >>> 2
    
    get(d, 'd', 'NA')
    >>> NA
    
    # get a inside b
    d = {'a': {'b': [{'a': 3}, {'c': 2}]}}
    get(d, ('b', 'a'), 'NA')
    >>> 3
    

使用 Python 测试:3.4.2 和 2.7.9

【讨论】:

  • 谢谢你。你的第一个例子给了我一个例外,但第二个例子很好。错误(Python 2 vs 3?):Traceback (most recent call last): File "untitled text 21", line 23, in <module> myGET(d).get('a', {}).get('b', []).get('c', 'NA') File "untitled text 21", line 14, in get _attr = object.__getattribute__(self, key) AttributeError: 'instance' object has no attribute 'a'
  • @DaveL17:是的 2.7 问题,缺少子类 class myGET(object):,已修复。
猜你喜欢
  • 2012-05-19
  • 1970-01-01
  • 2011-06-01
  • 1970-01-01
  • 2021-04-27
  • 1970-01-01
  • 2023-01-29
  • 1970-01-01
  • 2018-12-05
相关资源
最近更新 更多