Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 了解字典的深度_Python_Dictionary_Depth - Fatal编程技术网

Python 了解字典的深度

Python 了解字典的深度,python,dictionary,depth,Python,Dictionary,Depth,假设我们有这样一句话: d = {'a':1, 'b': {'c':{}}} 知道它的嵌套深度最直接的方法是什么?您必须遍历字典。你可以通过排队来做到这一点;以下内容应避免循环引用: from collections import deque def depth(d): queue = deque([(id(d), d, 1)]) memo = set() while queue: id_, o, level = queue.popleft()

假设我们有这样一句话:

d = {'a':1, 'b': {'c':{}}}

知道它的嵌套深度最直接的方法是什么?

您必须遍历字典。你可以通过排队来做到这一点;以下内容应避免循环引用:

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
请注意,因为我们以第一顺序访问所有字典值,
级别
值只会上升。
memo
集合用于确保我们不会无休止地遍历循环引用

或者可以使用递归遍历树(它有效地将函数调用用作堆栈)。我已经使用了便于扩展到其他容器类型的功能:

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())
这是深度优先搜索,因此现在需要
max()。每种深度有3个键的词典应反映该级别的最大深度

两个版本中使用的
memo
集都跟踪对象ID,因此如果执行类似
foo={}的操作,我们不会运行is circles;foo[“bar”]=foo

演示:

递归
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'})
迭代解决方案:

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'})

它可能会分支,或者每层只有一个键?您担心的只是嵌套的dict,或者dict的值也可能是其他容器吗?对于您给出的示例,我会说(最直接的)答案是查看它。另外,我不敢相信这不是一个重复的+1的可能的重复(但它似乎检查了!)一个警告:应该无和{}返回1吗?但这是一个惯例问题。@lukas:技术可以调整;重点更在于说明需要做些什么。这里的关键点是遍历(在深度优先时使用'max()),我想说。level的默认值应该是0,而不是1。一个简单的dict返回2作为深度,这是不正确的。同样对于无和空的dicts,深度应该是0而不是1。@samyarus:这就是所有的解释;如果顶级字典为空怎么办?深度是0还是1?字典已经得到值这一事实可以看作是另一个层面。空字典->1,带值字典->2(字典本身需要一个级别的引用,值需要另一个级别的引用)。@Samyarus:您可以通过将
level
的起始值调整为0,并为非字典值返回
level
,为空字典返回
level+1
来获得解释。但是请注意,OP没有指定深度的计算方式,所以我们都可以自由地进行自己的解释。为什么在这里使用两个队列?向右追加和向左弹出的本质意味着单个队列的行为完全相同。@MartijnPieters为了计算最大值,不需要第二个队列。如果您想打印每个级别及其最大深度,这将非常有用。不过,您也不需要使用两个队列。只需在队列中包含一个深度,并在处理队列时打印新值(例如,当下一个级别高于上一个级别时)。非常优雅的解决方案如果需要
,那么
为什么是
呢?@baxx,如果字典为空(
d=={}
d
是falsy>)
max(…)
针对空iterable的调用导致ValueError<代码>。。。如果d else..
用于防止出现这种情况。试试
max([])
@baxx,如果你使用Python 3.4+(其中
default
关键字参数添加到
max
),你可以使用
max(map(depth,d.values()),default=0)
。啊,好的,我想我试过了,没有空字典,谢谢你提供的信息