web-dev-qa-db-ja.com

連鎖され、ネストされたdict()はpython

Dict.get( 'keyword')メソッドを使用してネストされた辞書に問い合わせています。現在、私の構文は...

M = cursor_object_results_of_db_query

for m in M:
    X = m.get("gparents").get("parent").get("child")
    for x in X:
        y = x.get("key")

ただし、「親」または「子」タグのいずれかが存在せず、スクリプトが失敗する場合があります。 get()を使用していることは知っていますが、キーがフォームに存在しない場合に備えて、デフォルトを含めることができます...

get("parent", '') or
get("parent", 'Orphan') 

しかし、Null''、または空の値を含めると、""にはメソッド.get("child")がないため、''.get("child")で呼び出されたときにチェーン.get()は失敗します。 。

私が今これを解決している方法は、各.get("")呼び出しの周りに一連のシーケンシャルtry-exceptを使用することですが、それは愚かでunpythonのようです---デフォルトで"skip"または"pass"またはそれでも何かを返す方法はありますか存在しないキーを深く掘り下げるのではなく、チェーンをサポートし、インテリジェントに失敗しますか?

理想的には、これをフォームのリスト内包表記にしたいと思います。

[m.get("gparents").get("parent").get("child") for m in M]

しかし、親がいないために.get("child")呼び出しが発生してプログラムが終了した場合、これは現在不可能です。

24
Mittenchops

これらはすべてpython dictsであり、それらに対してdict.get()メソッドを呼び出しているため、空のdictを使用してチェーンを作成できます。

_[m.get("gparents", {}).get("parent", {}).get("child") for m in M]
_

最後の.get()のデフォルトをオフのままにすると、Noneにフォールバックします。これで、中間キーのいずれかが見つからない場合、チェーンの残りの部分は空の辞書を使用して検索を行い、.get('child')で終了してNoneを返します。

69
Martijn Pieters

別のアプローチは、キーが見つからない場合、dict.getNoneを返すことを認識することです。ただし、Noneには属性.getがないため、AttributeErrorがスローされます。

for m in M:
    try:
       X = m.get("gparents").get("parent").get("child")
    except AttributeError:
       continue

    for x in X:
        y = x.get("key")
        #do something with `y` probably???

Martijnの答えと同じように、これはXが反復可能(非None)であることを保証するものではありません。ただし、チェーンの最後のgetをデフォルトで空のリストを返すようにすることで、これを修正できます。

 try:
    X = m.get("gparents").get("parent").get("child",[])
 except AttributeError:
    continue

最後に、この問題のおそらく最善の解決策はreduceを使用することだと思います。

try:
    X = reduce(dict.__getitem__,["gparents","parent","child"],m)
except (KeyError,TypeError):
    pass
else:
    for x in X:
       #do something with x

ここでの利点は、発生した例外のタイプに基づいて、getsのいずれかが失敗したかどうかを知ることができることです。 getが間違ったタイプを返す可能性があり、その場合はTypeErrorを取得します。ただし、辞書にキーがない場合は、KeyErrorが発生します。それらは別々にまたは一緒に扱うことができます。ユースケースに最適なものは何でも。

8
mgilson

小さなヘルパー関数を使ってみませんか?

def getn(d, path):
    for p in path:
        if p not in d:
            return None
        d = d[p]
    return d

その後

[getn(m, ["gparents", "parent", "child"]) for m in M]
4
georg

私はその部分に少し遅れていることに気づきましたが、同様の問題に直面したときに私が思いついた解決策は次のとおりです。

def get_nested(dict_, *keys, default=None):
    if not isinstance(dict_, dict):
        return default
    elem = dict_.get(keys[0], default)
    if len(keys) == 1:
        return elem
    return get_nested(elem, *keys[1:], default=default)

例えば:

In [29]: a = {'b': {'c': 1}}
In [30]: get_nested(a, 'b', 'c')
Out[30]: 1
In [31]: get_nested(a, 'b', 'd') is None
Out[31]: True
4
Kevin Dungs