【问题标题】:recursively traverse multidimensional dictionary, dimension unknown递归遍历多维字典,维度未知
【发布时间】:2011-04-21 02:55:54
【问题描述】:

我想创建一个函数来递归遍历一个维度未知的多维字典。

这是我目前为止的想法,但它似乎无法正常工作。这将打印出一些键/值两次,它们不是按顺序排列的。

def walk_dict(d):
    for k,v in d.items():
        if isinstance(v, dict):
            walk_dict(v)
        else:
            print "%s %s" % (k, v) 

这是一个示例数组:

d = {
        'plan_code': 'b',
        'quantity': '1',
        'account': {
            'account_code': 'b',
            'username': 'jdoe',
            'email': 'jdoe@domain.com',
            'first_name': 'b',
            'last_name': 'b',
            'company_name': 'Company, LLC.',
            'billing_info': {
                'first_name': 'b',
                'last_name': 'b',
                'address1': '123 Test St',
                'city': 'San Francisco',
                'state': 'CA',
                'country': 'US',
                'zip': '94105',
                'credit_card': {
                    'number': '1',
                    'year': '2018',
                    'month': '12',
                    'verification_value': '123',
                },
            },
        },
    }

【问题讨论】:

    标签: python


    【解决方案1】:

    这确实会正确打印键值对。你能指出哪些数据被重复。根据上述数据可能会产生混淆,因为这些键:

    'first_name': 'b',
    'last_name': 'b',
    

    是两个字典的一部分 - 'account' 和 'billing_info'。所以它们会在输出中出现两次。

    此外,如果您想要某种顺序来打印您的字典 K、V,请使用 Ordered Dictionaries

    【讨论】:

    • 我只是抓住了那个字典,并没有注意到重复。以为我做错了什么。感谢您指出这一点。
    【解决方案2】:

    我不确定你的最终目标是什么,但代码正在做它应该做的事情。您看到的是您认为重复的项目,因为有键/值组合,例如 'first_name':'b',它们都在 'account' 和 'account' 的 'billing_info' 中。我不确定您要查找的顺序是什么,但字典是无序的,因此您打印出来的函数必须给它们一些顺序,例如通过替换以下内容:

    for k,v in d.items():
    

    for k,v in sorted(d.items(),key=lambda x: x[0]):
    

    或者你需要一个有序的字典。你也可以像这样使用 pprint 模块来打印出一个漂亮的字典:

    >>> import pprint
    >>> pprint.pprint(d)
    {'account': {'account_code': 'b',
                 'billing_info': {'address1': '123 Test St',
                                  'city': 'San Francisco',
                                  'country': 'US',
                                  'credit_card': {'month': '12',
                                                  'number': '1',
                                                  'verification_value': '123',
                                                  'year': '2018'},
                                  'first_name': 'b',
                                  'last_name': 'b',
                                  'state': 'CA',
                                  'zip': '94105'},
                 'company_name': 'Company, LLC.',
                 'email': 'jdoe@domain.com',
                 'first_name': 'b',
                 'last_name': 'b',
                 'username': 'jdoe'},
     'plan_code': 'b',
     'quantity': '1'}
    

    但是,我不完全确定您的最终目标是什么。此外,当值是字典时,您会丢失键。我修改了您的代码以执行与 pprint 在以下操作中所做的类似的事情:

    def walk_dict(d,depth=0):
        for k,v in sorted(d.items(),key=lambda x: x[0]):
            if isinstance(v, dict):
                print ("  ")*depth + ("%s" % k)
                walk_dict(v,depth+1)
            else:
                print ("  ")*depth + "%s %s" % (k, v) 
    

    对于您的示例 dict 产生:

    >>> walk_dict(d)
    account
      account_code b
      billing_info
        address1 123 Test St
        city San Francisco
        country US
        credit_card
          month 12
          number 1
          verification_value 123
          year 2018
        first_name b
        last_name b
        state CA
        zip 94105
      company_name Company, LLC.
      email jdoe@domain.com
      first_name b
      last_name b
      username jdoe
    plan_code b
    quantity 1
    

    【讨论】:

      【解决方案3】:

      您的代码运行良好,完全按照您的指示运行。

      我在输出中看到的唯一重复是 first_name 和 last_name 实际上定义了两次(在不同的字典中)。

      至于'乱序'它是一本字典,它没有保证的顺序。好吧,也许确实如此,但它将基于内部表示,您不应该依赖它。

      您需要做的是按照您想要的方式对数据进行排序。您可能还想打印出子字典的键值以使输出更易于理解。

      【讨论】:

        【解决方案4】:

        作为Justin Peel mentionspprint.pprint 可能会做你想做的事。

        我认为您的代码的问题是您应该在递归之前先打印密钥,即更改

            if isinstance(v, dict):
                walk_dict(v)
        

            if isinstance(v, dict):
                print k
                walk_dict(v)
        

        尽管在任何情况下它都会看起来很混乱,除非你添加缩进等。

        这种事情其实很复杂;如果您想获得一些想法,请查看pprint 的代码。

        【讨论】:

          【解决方案5】:

          在 Python 中,字典由键索引。键可以是任何不可变类型,例如字符串或数字。除非对键进行排序,否则它们将始终以任意顺序返回。由于这个事实,您的walk_dict 正在打印看似随机的结果。

          这是一个walk_dict 的示例,它打印所有键和值。我已经在字典的每个级别对它们的键进行了排序。另外,我打印了每个键。您的代码在递归之前没有打印密钥。最后,我添加了字符串填充以强调字典的每个级别。 doctests 都通过了。我希望这可以帮助您构建最终功能。

          import doctest
          
          
          def walk_dict(seq, level=0):
              """Recursively traverse a multidimensional dictionary and print all
              keys and values.
          
              >>> d = {'dog': 'dusty', 'cat': 'fluffy', 'bird': 'chirpy'}
              >>> walk_dict(d)
              bird chirpy
              cat fluffy
              dog dusty
              >>> d = {'location': 'home', 'animals':{'dog': 'dusty', 'cat': 'fluffy', 'bird': 'chirpy'}}
              >>> walk_dict(d)
              animals
                bird chirpy
                cat fluffy
                dog dusty
              location home
              >>> d = {'location': 'home', 'animals':{'dog': 'dusty', 'cat': 'fluffy', 'bird': {'name':'chirpy', 'color':'blue'}}}
              >>> walk_dict(d)
              animals
                bird
                  color blue
                  name chirpy
                cat fluffy
                dog dusty
              location home
              >>> d = { \
                      'plan_code': 'b', \
                      'quantity': '1', \
                      'account': { \
                          'account_code': 'b', \
                          'username': 'jdoe', \
                          'email': 'jdoe@domain.com', \
                          'first_name': 'b', \
                          'last_name': 'b', \
                          'company_name': 'Company, LLC.', \
                          'billing_info': { \
                              'first_name': 'b', \
                              'last_name': 'b', \
                              'address1': '123 Test St', \
                              'city': 'San Francisco', \
                              'state': 'CA', \
                              'country': 'US', \
                              'zip': '94105', \
                              'credit_card': { \
                                  'number': '1', \
                                  'year': '2018', \
                                  'month': '12', \
                                  'verification_value': '123', \
                              }, \
                          }, \
                      }, \
                  } 
              >>> walk_dict(d)
              account
                account_code b
                billing_info
                  address1 123 Test St
                  city San Francisco
                  country US
                  credit_card
                    month 12
                    number 1
                    verification_value 123
                    year 2018
                  first_name b
                  last_name b
                  state CA
                  zip 94105
                company_name Company, LLC.
                email jdoe@domain.com
                first_name b
                last_name b
                username jdoe
              plan_code b
              quantity 1
              """
              items = seq.items()
              items.sort()
              for v in items:
                  if isinstance(v[1], dict):
                      # Print the key before make a recursive call
                      print "%s%s" % ("  " * level, v[0])
                      nextlevel = level + 1
                      walk_dict(v[1], nextlevel)
                  else:
                      print "%s%s %s" % ("  " * level, v[0], v[1])
          
          
          if __name__ == '__main__':
              doctest.testmod()
          

          【讨论】:

            【解决方案6】:
            import json
            print(json.dumps(d, indent=4))
            

            【讨论】:

            • 就我而言,我需要这样做正是因为json.dumps 无法处理结构中的某些对象。
            • jsonpickle 库在表示对象类型方面做得非常出色 json 阻塞 - 试试吧。
            【解决方案7】:

            这是 Justin Peel 接受的答案的变体,它以 OrderedDict 的形式返回其结果,而不是打印结果。

            from collections import OrderedDict
            
            def sort_by_keys(dct,):
                new_dct = OrderedDict({})
                for item in sorted(dct.items(), key=lambda (key, val): key):
                    key = item[0]
                    val = item[1]
                    if isinstance(val, dict):
                        new_dct[key] = sort_by_keys(val)
                    else:
                        new_dct[key] = val
                return new_dct
            

            【讨论】:

            • Python 3 不支持元组解包
            【解决方案8】:

            到目前为止提到的解决方案并没有达到我想要的效果,但是根据 Justin Peel 的拼写版本,我能够制作出以我想要的方式打印的东西:

            def dict_parser(data, depth=0):
                if isinstance(data, dict):
                    for key in data:
                        value = data[key]
                        print('  ') * depth + '"' + key + '":'
                        dict_parser(value, depth + 1)
                elif isinstance(data, list):
                    for item in data:
                        if isinstance(item, list):
                            dict_parser(item, depth + 1)
                        else:
                            print('  ') * depth + '"' + item + '"'
                elif isinstance(data, bool):
                    print('  ') * depth + str(data)
                else:
                    print('  ') * depth + '"' + data + '"'
            

            其中打印了类似的内容:

            "saved_skels":
              "E:\Child_Skeleton_6ft.fbx"
              "E:\Female_Skeleton_6ft.fbx"
              "E:\HeroFemale_Skeleton.fbx"
              "E:\HeroMale_Skeleton.fbx"
            "saved_roots":
              "E:\users\Characters"
              "E:\content\characters"
            "saved_tabs":
              "Characters":
                "ProxyFemale":
                  "HeroFemale_Skeleton.fbx"
                "animation":
                  "HeroFemale_Skeleton.fbx"
                "ProxyMale":
                  "HeroMale_Skeleton.fbx"
              "Game":
                ""
              "Proto":
                "Female":
                  "Female_Skeleton_NewBP.fbx"
              "Assets":
                ""
              "characters":
                "player":
                  "player_Skeleton.fbx"
                "actor":
                  "Hero_Skeleton.fbx"
            "edit_common_checkbox_state":
              False
            "get_latest_checkbox_state":
              True
            

            【讨论】:

              猜你喜欢
              • 2014-07-19
              • 2021-01-18
              • 2010-11-03
              • 1970-01-01
              • 1970-01-01
              • 2013-03-04
              • 2019-08-03
              • 2012-07-17
              • 1970-01-01
              相关资源
              最近更新 更多