各レベルを個別にチェックせずに、辞書に埋め込まれたキーが存在するかどうかを確認するより読みやすい方法はありますか?
埋められたオブジェクトにこの値を取得する必要があるとしましょう(Wikidataからの例):
x = s['mainsnak']['datavalue']['value']['numeric-id']
これが実行時エラーで終了しないことを確認するには、次のようにすべてのレベルをチェックする必要があります。
if 'mainsnak' in s and 'datavalue' in s['mainsnak'] and 'value' in s['mainsnak']['datavalue'] and 'nurmeric-id' in s['mainsnak']['datavalue']['value']:
x = s['mainsnak']['datavalue']['value']['numeric-id']
これを解決するために考えられるもう1つの方法は、これをtry catch
コンストラクトにラップすることです。
私は次のようなものを探しています:
x = exists(s['mainsnak']['datavalue']['value']['numeric-id'])
すべてのレベルが存在する場合、True
を返します。
簡単に言うと、Pythonを使用すると、信頼できる必要があります 許可よりも許しを求める方が簡単です
try:
x = s['mainsnak']['datavalue']['value']['numeric-id']
except KeyError:
pass
ネストされたdictキーの処理方法は次のとおりです。
def keys_exists(element, *keys):
'''
Check if *keys (nested) exists in `element` (dict).
'''
if not isinstance(element, dict):
raise AttributeError('keys_exists() expects dict as first argument.')
if len(keys) == 0:
raise AttributeError('keys_exists() expects at least two arguments, one given.')
_element = element
for key in keys:
try:
_element = _element[key]
except KeyError:
return False
return True
例:
data = {
"spam": {
"Egg": {
"bacon": "Well..",
"sausages": "Spam Egg sausages and spam",
"spam": "does not have much spam in it"
}
}
}
print 'spam (exists): {}'.format(keys_exists(data, "spam"))
print 'spam > bacon (do not exists): {}'.format(keys_exists(data, "spam", "bacon"))
print 'spam > Egg (exists): {}'.format(keys_exists(data, "spam", "Egg"))
print 'spam > Egg > bacon (exists): {}'.format(keys_exists(data, "spam", "Egg", "bacon"))
出力:
spam (exists): True
spam > bacon (do not exists): False
spam > Egg (exists): True
spam > Egg > bacon (exists): True
指定された順序で各キーをテストして、指定されたelement
をループします。
EAFP に続くため、私が見つけたすべてのvariable.get('key', {})
メソッドよりもこれを好む。
keys_exists(dict_element_to_test, 'key_level_0', 'key_level_1', 'key_level_n', ..)
のように呼び出されることを除く関数。少なくとも2つの引数、要素と1つのキーが必要ですが、必要なキーの数を追加できます。
ある種のマップを使用する必要がある場合、次のようなことができます。
expected_keys = ['spam', 'Egg', 'bacon']
keys_exists(data, *expected_keys)
デフォルトで.get
を使用できます:
s.get('mainsnak', {}).get('datavalue', {}).get('value', {}).get('numeric-id')
しかし、これはほぼ確実にtry/exceptを使用する場合ほど明確ではありません。
Try/exceptは、これを行うための最もPython的な方法のようです。
次の再帰関数が機能するはずです(キーの1つが辞書に見つからなかった場合はNoneを返します)
def exists(obj, chain):
_key = chain.pop(0)
if _key in obj:
return exists(obj[_key], chain) if chain else obj[_key]
myDict ={
'mainsnak': {
'datavalue': {
'value': {
'numeric-id': 1
}
}
}
}
result = exists(myDict, ['mainsnak', 'datavalue', 'value', 'numeric-id'])
print(result)
>>> 1
python-benedict
、完全なキーパスサポートおよび多くのユーティリティメソッドを備えた堅実なpython dictサブクラスを使用することをお勧めします。
既存の辞書をキャストするだけです:
s = benedict(s)
これで、dictは完全なキーパスをサポートし、キーがPythonの方法で存在するかどうかを確認できますin演算子を使用:
if 'mainsnak.datavalue.value.numeric-id' in s:
# do stuff
ライブラリリポジトリとドキュメント: https://github.com/fabiocaccamo/python-benedict
pydash
を使用して、存在するかどうかを確認できます。 http://pydash.readthedocs.io/en/latest/api.html#pydash.objects.has
または、値を取得します(デフォルトを設定することもできます-存在しない場合は戻ります): http://pydash.readthedocs.io/en/latest/api.html#pydash.objects.has
以下に例を示します。
>>> get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c[1]')
2
Try/exceptの方法は最もクリーンで、コンテストはありません。ただし、IDEでも例外としてカウントされ、デバッグ中に実行が停止します。
さらに、メソッド内の制御ステートメントとして例外を使用することは好きではありません。これは、基本的にtry/catchで発生していることです。
再帰を使用せず、デフォルト値をサポートする短いソリューションを次に示します。
def chained_dict_lookup(lookup_dict, keys, default=None):
_current_level = lookup_dict
for key in keys:
if key in _current_level:
_current_level = _current_level[key]
else:
return default
return _current_level
オブジェクトパスの文字列表現のテストに苦しむことができる場合、このアプローチはあなたのために働くかもしれません:
def exists(str):
try:
eval(str)
return True
except:
return False
exists("lst['sublist']['item']")
このような場合のために dataknead
と呼ばれるデータ解析ライブラリを作成しました。これは、基本的に、Wikidata APIが返すJSONにもイライラしたためです。
そのライブラリを使用すると、次のようなことができます
from dataknead import Knead
numid = Knead(s).query("mainsnak/datavalue/value/numeric-id").data()
if numid:
# Do something with `numeric-id`