各レベルを個別にチェックすることなく、dictに埋もれたキーが存在するかどうかをチェックする、より読みやすい方法はないでしょうか?
例えば、この値を埋もれたオブジェクトで取得する必要があるとします(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']
これを解決するために私が思いつく他の方法は、これを try catch
構造にラップすることですが、このような単純なタスクにはかなり厄介だと感じます。
みたいなのを探しています:
x = exists(s['mainsnak']['datavalue']['value']['numeric-id'])
これは、すべてのレベルが存在する場合に True
を返します。
簡単に言うと、Pythonの場合、信頼しなければならないのは【許可よりも許しを請う方が簡単】です(https://docs.python.org/2/glossary.html#term-eafp)
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](https://docs.python.org/2/glossary.html#term-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)
Try/exceptは最もパイソン的な方法と思われます。
以下のような再帰的な関数が動作するはずです(キーの1つがdictで見つからなかった場合は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ディクテクトサブクラスである「python-benedict」を使用することをお勧めします。
既存のディクストをキャストするだけです。
s = benedict(s)
これで、ディクテクトに完全なキーパスがサポートされ、キーがピトニックな方法で存在するかどうかを確認できます。 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
試行/例外方法は最もクリーンで、コンテストはありません。 ただし、これはIDEの例外としてもカウントされ、デバッグ中に実行が停止します。
さらに、例外をメソッド内制御ステートメントとして使用するのは好きではありません。これは、本質的にトライ/キャッチで起こっていることです。
以下は、再帰を使用せず、デフォルト値をサポートする短いソリューションです。
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
私も同じ問題を抱えていて、最近python libがポップアップしました。 https://pypi.org/project/dictor/。 https://github.com/perfecto25/dictor。
だからあなたの場合: ``。 ディクターからディクターをインポートします。
x =ディクター、「mainsnak.datavalue.value.nu meric-id」)。 ``。
----------。
個人メモ:。 それが実際に何をしているのかを示唆していないので、私は「独裁者」の名前が好きではありません。 だから私はそれを次のように使用しています。
。 ディクターからディクターを抽出としてインポートします。 x =抽出物、「mainsnak.datavalue.value.nu meric-id」)。
。
「抽出」よりも優れた命名法を考え出すことができませんでした。 より実行可能な命名法を考え出した場合は、自由にコメントしてください。 safe_get
、 robust_get
は私のケースに適切であると感じませんでした。
このような場合、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`