私たちがこの口述を持っていると仮定します:
d = {'a':1, 'b': {'c':{}}}
ネストを知る最も簡単な方法は何でしょうかdepthそれの?
辞書をトラバースする必要があります。キューを使用してこれを行うことができます。以下は循環参照から安全である必要があります。
_from collections import deque
def depth(d):
queue = deque([(id(d), d, 1)])
memo = set()
while queue:
id_, o, level = queue.popleft()
if id_ in memo:
continue
memo.add(id_)
if isinstance(o, dict):
queue += ((id(v), v, level + 1) for v in o.values())
return level
_
すべてのディクショナリ値にbreath-firstの順序でアクセスするため、level
値は上昇するだけであることに注意してください。 memo
セットは、循環参照を際限なくトラバースしようとしないようにするために使用されます。
または、再帰を使用してツリーをトラバースすることもできます(これは関数呼び出しをスタックとして効果的に使用します)。他のコンテナタイプに簡単に拡張するために functools.singledispatch()
を使用しました:
_from functools import singledispatch, wraps
@singledispatch
def depth(_, _level=1, _memo=None):
return _level
def _protect(f):
"""Protect against circular references"""
@wraps(f)
def wrapper(o, _level=1, _memo=None, **kwargs):
_memo, id_ = _memo or set(), id(o)
if id_ in _memo: return _level
_memo.add(id_)
return f(o, _level=_level, _memo=_memo, **kwargs)
return wrapper
def _protected_register(cls, func=None, _orig=depth.register):
"""Include the _protect decorator when registering"""
if func is None and isinstance(cls, type):
return lambda f: _orig(cls, _protect(f))
return _orig(cls, _protect(func)) if func is not None else _orig(_protect(cls))
depth.register = _protected_register
@depth.register
def _dict_depth(d: dict, _level=1, **kw):
return max(depth(v, _level=_level + 1, **kw) for v in d.values())
_
これは深さ優先探索であるため、それぞれで精査中のcurrentオブジェクトの最大深度を選択するには、max()
が必要です。レベル。それぞれ異なる深さの3つのキーを持つ辞書は、そのレベルで最大の深さを反映する必要があります。
どちらのバージョンでも使用されているmemo
セットはオブジェクトIDを追跡するため、_foo = {}; foo["bar"] = foo
_のようなことをした場合は円は実行されません。
デモ:
_>>> d = {'a':1, 'b': {'c':{}}}
>>> depth(d)
3
>>> d = {'foo': {'bar': {'baz': 0}, 'spam': {'ham': {'monty': 1}, 'eric': 'idle'}}, 'john': 'cleese'}
>>> depth(d)
5
>>> circular = {}
>>> circular["self"] = circular
>>> depth(circular)
2
_
再帰的なsingledispatch
バージョンは、リストなど、より多くのコンテナーをカバーするように拡張できます。
_@depth.register
def _list_depth(l: list, _level=1, **kw):
return max(depth(v, _level=_level + 1, **kw) for v in l)
_
循環参照テストを処理するために標準の_.register
_デコレータを拡張したので、追加のコンテナサポートの実装は比較的簡単です。追加のキーワード引数を再帰呼び出しに渡すことを忘れないでください!
再帰関数を作成する必要があります。
>>> def depth(d):
... if isinstance(d, dict):
... return 1 + (max(map(depth, d.values())) if d else 0)
... return 0
...
>>> d = {'a':1, 'b': {'c':{}}}
>>> depth(d)
3
非再帰的ソリューション:
def depth(d):
depth=0
q = [(i, depth+1) for i in d.values() if isinstance(i, dict)]
max_depth = 0
while (q):
n, depth = q.pop()
max_depth = max(max_depth, depth)
q = q + [(i, depth+1) for i in n.values() if isinstance(i, dict)]
print max_depth
反復ソリューション:
from collections import deque
def depth(d):
q = deque([d])
q2 = deque()
max_depth = 0
while q:
curr_dict = q.popleft()
if isinstance(curr_dict, dict):
for di in curr_dict.itervalues():
q2.append(di)
if not q:
q, q2 = q2, q
max_depth += 1
return max_depth
print depth(None)
print depth({})
print depth({"a": "b"})
print depth({"a": "b", "c": {"d": "e"}, "f": {"g": "h"}, "i": {"j": "k"}, "x": {}, "z": {} })
print depth({'a':1, 'b': {'c':{}}})
print depth({'foo': {'bar': {'baz': 0}, 'spam': {'ham': {'monty': 1}, 'eric': 'idle'}}, 'john': 'cleese'})