중첩 된 사전이 있습니다. 안전하게 가치를 얻는 방법은 하나뿐입니까?
try:
example_dict['key1']['key2']
except KeyError:
pass
아니면 파이썬에는 get()
중첩 사전 과 같은 방법이 있습니까?
except keyerror:
절 에서 항상 기본값을 지정할 수 있습니다 .
중첩 된 사전이 있습니다. 안전하게 가치를 얻는 방법은 하나뿐입니까?
try:
example_dict['key1']['key2']
except KeyError:
pass
아니면 파이썬에는 get()
중첩 사전 과 같은 방법이 있습니까?
except keyerror:
절 에서 항상 기본값을 지정할 수 있습니다 .
답변:
get
두 번 사용할 수 있습니다 .
example_dict.get('key1', {}).get('key2')
존재 하거나 존재하지 않으면 반환 None
됩니다 .key1
key2
이것은 여전히 존재하지만 dict (또는 메소드가 있는 dict-like 객체)가 아닌 AttributeError
경우 발생할 수 있습니다 . 설명 할 수없는 경우 게시 한 코드가 대신 나타납니다 .example_dict['key1']
get
try..except
TypeError
example_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)
쉽게 Hasher
a를 일반으로 변환하십시오 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')
safeget
방법은 원래 사전을 덮어 쓰기 때문에 매우 안전하지 않은 방법으로 여러 가지가 있습니다. 이는 다음과 같은 작업을 안전하게 수행 할 수 없음을 의미합니다 safeget(dct, 'a', 'b') or safeget(dct, 'a')
.
dct = dct[key]
다시 할당 합니다 dct
. 이렇게하면 원래 dict이 변경되지 않습니다. 따라서 원래 dict는 영향을받지 않습니다 safeget
. 반면에 dct[key] = ...
dict가 사용 된 경우 원래 dict가 수정되었을 것입니다. 즉, 파이썬에서 이름은 values에 바인딩됩니다 . 이름에 새 값을 할당해도 이전 값에 영향을 미치지 않습니다 (이전 값에 대한 참조가 더 이상없는 경우 (CPython에서 가비지 수집 됨))
safeget
또한 경우에 중첩 딕셔너리의 키가 실패 할 방법은 존재하지만,이 값은 널 (null)이다. TypeError: 'NoneType' object is not subscriptable
다음 번 반복에 던져 질 것입니다
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
>>>
deep_get({'a': 1}, "a.b")
제공 None
하지만 나는 같은 예외 기대 KeyError
또는 뭔가.
None
Raise KeyError
재귀 솔루션. 가장 효율적이지는 않지만 다른 예제보다 약간 더 읽기 쉽고 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에서 잘 테스트되고 공개 소스입니다 .
d.get('a.b[0].c[-1]')
dict을 감싸고 키를 기반으로 검색 할 수있는 간단한 클래스입니다.
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
기본적으로 반환 됩니다. 래퍼 의 default=
키 를 사용하여이를 무시할 수 있습니다 ( FindDict
예 :`).
FindDict(person, default='').get('person.name.last') # == doesn't exist, so ''
속성을 깊이 얻는 것에 대해 이것을 본 후에 dict
도트 표기법을 사용하여 중첩 된 값 을 안전하게 얻도록 다음을 만들었습니다 . dicts
deserialized 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)
fallback
실제로는 함수에서 사용되지 않습니다.
.
sep=','
주어진 (sep, fallback) 조건에 대해 일반화 할 키워드 인수를 추가 할 수 있습니다 . 그리고 @denvar 는 축소 시퀀스 후에 obj
type이라고 하면 int
obj [name]은 TypeError를 발생시킵니다. obj.get (name) 또는 obj.get (name, fallback)을 대신 사용하면 AttributeError가 발생하므로 잡을 필요가 있습니다.
같은 것에 대한 또 다른 함수는 키를 찾았는지 여부를 나타내는 부울을 반환하고 예기치 않은 오류를 처리합니다.
'''
json : json to extract value from if exists
path : details.detail.first_name
empty path represents root
returns a tuple (boolean, object)
boolean : True if path exists, otherwise False
object : the object if path exists otherwise None
'''
def get_json_value_at_path(json, path=None, default=None):
if not bool(path):
return True, json
if type(json) is not dict :
raise ValueError(f'json={json}, path={path} not supported, json must be a dict')
if type(path) is not str and type(path) is not list:
raise ValueError(f'path format {path} not supported, path can be a list of strings like [x,y,z] or a string like x.y.z')
if type(path) is str:
path = path.strip('.').split('.')
key = path[0]
if key in json.keys():
return get_json_value_at_path(json[key], path[1:], default)
else:
return False, default
사용법 예 :
my_json = {'details' : {'first_name' : 'holla', 'last_name' : 'holla'}}
print(get_json_value_at_path(my_json, 'details.first_name', ''))
print(get_json_value_at_path(my_json, 'details.phone', ''))
(참, '홀라')
(거짓, '')
pydash를 사용할 수 있습니다 :
import pydash as _
_.get(example_dict, 'key1.key2', default='Default')
중첩 된 사전 / JSON 조회의 경우 독재자를 사용할 수 있습니다.
핍 설치 독재자
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"
]
}
}
}
Lonestar의 항목을 얻으려면 간단히 점으로 구분 된 경로를 제공하십시오.
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']
키가 경로에없는 경우 대체 값을 제공 할 수 있습니다.
소문자를 무시하고 '.'이외의 다른 문자를 사용하는 등 더 많은 옵션을 사용할 수 있습니다. 경로 구분자로
나는 이 대답을 거의 바꾸지 않았다 . 우리는 숫자가있는 목록을 사용하고 있는지 확인하는 것을 추가했습니다. 이제 어느 쪽이든 사용할 수 있습니다. deep_get(allTemp, [0], {})
또는 deep_get(getMinimalTemp, [0, minimalTemperatureKey], 26)
등
def deep_get(_dict, keys, default=None):
def _reducer(d, key):
if isinstance(d, dict):
return d.get(key, default)
if isinstance(d, list):
return d[key] if len(d) > 0 else default
return default
return reduce(_reducer, keys, _dict)
이미 많은 좋은 답변이 있지만 색인으로 목록에 도달하는 것을 지원하는 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