[python] 중첩 된 사전의 가치를 얻는 파이썬 안전 방법

중첩 된 사전이 있습니다. 안전하게 가치를 얻는 방법은 하나뿐입니까?

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

아니면 파이썬에는 get()중첩 사전 과 같은 방법이 있습니까?



답변

get두 번 사용할 수 있습니다 .

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

존재 하거나 존재하지 않으면 반환 None됩니다 .key1key2

이것은 여전히 존재하지만 dict (또는 메소드가 있는 dict-like 객체)가 아닌 AttributeError경우 발생할 수 있습니다 . 설명 할 수없는 경우 게시 한 코드가 대신 나타납니다 .example_dict['key1']gettry..exceptTypeErrorexample_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'>

키가 없으면 빈 Hasher가 반환됩니다.

당신 Hasher의 하위 클래스 이기 때문에 dict당신은 Hasher를 사용할 수있는 것과 거의 같은 방식으로 Hasher를 사용할 수 있습니다 dict. 동일한 방법과 구문을 모두 사용할 수 있으며 해셔는 누락 된 키를 다르게 취급합니다.

정규식 dict을 다음 Hasher과 같이 변환 할 수 있습니다 .

hasher = Hasher(example_dict)

쉽게 Hashera를 일반으로 변환하십시오 dict.

regular_dict = dict(hasher)

또 다른 대안은 도우미 기능에서 추함을 숨기는 것입니다.

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

따라서 나머지 코드는 비교적 읽을 수 있습니다.

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


답변

python reduce를 사용할 수도 있습니다 .

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


답변

여기에있는 모든 대답과 내가 작성한 작은 변경 사항을 결합하면이 기능이 유용 할 것이라고 생각합니다. 안전하고 빠르며 쉽게 관리 할 수 ​​있습니다.

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)

예 :

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


답변

Yoav의 답변을 기반으로 한 더 안전한 접근 방식 :

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


답변

재귀 솔루션. 가장 효율적이지는 않지만 다른 예제보다 약간 더 읽기 쉽고 functools에 의존하지 않습니다.

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

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

더 세련된 버전

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)


답변

감소 접근 방식은 깔끔하고 짧지 만 간단한 루프가 더 쉽다고 생각합니다. 기본 매개 변수도 포함했습니다.

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

one-liner 감소 작동 방식을 이해하기위한 연습으로 다음을 수행했습니다. 그러나 궁극적으로 루프 접근법은 더 직관적 인 것처럼 보입니다.

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)

용법

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

print deep_get(nested, ['a', 'b'])
print deep_get(nested, ['a', 'b', 'z', 'z'], default='missing')


답변

나는 당신이 시도하는 것이 좋습니다 python-benedict.

dict키 경로 지원 등을 제공 하는 서브 클래스입니다.

설치: pip install python-benedict

from benedict import benedict

example_dict = benedict(example_dict, keypath_separator='.')

이제 keypath를 사용하여 중첩 값에 액세스 할 수 있습니다 :

val = example_dict['key1.key2']

# using 'get' method to avoid a possible KeyError:
val = example_dict.get('key1.key2')

또는 키 목록을 사용하여 중첩 값에 액세스 하십시오 .

val = example_dict['key1', 'key2']

# using get to avoid a possible KeyError:
val = example_dict.get(['key1', 'key2'])

GitHub에서 잘 테스트되고 공개 소스입니다 .

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