【问题标题】:Nested dictionary value from key path来自键路径的嵌套字典值
【发布时间】:2015-09-11 01:38:28
【问题描述】:

借助键路径从嵌套字典中获取值,这里是dict

json = {
    "app": {
        "Garden": {
            "Flowers": {
                "Red flower": "Rose",
                "White Flower": "Jasmine",
                "Yellow Flower": "Marigold"
            }
        },
        "Fruits": {
            "Yellow fruit": "Mango",
            "Green fruit": "Guava",
            "White Flower": "groovy"
        },
        "Trees": {
            "label": {
                "Yellow fruit": "Pumpkin",
                "White Flower": "Bogan"
            }
        }
    }

该方法的输入参数是用点分隔的关键路径,从关键路径="app.Garden.Flowers.white Flower"需要打印'Jasmine'。到目前为止我的代码:

import json
with open('data.json') as data_file:    
  j = json.load(data_file)


def find(element, JSON):     
  paths = element.split(".")  
  # print JSON[paths[0]][paths[1]][paths[2]][paths[3]]
  for i in range(0,len(paths)):
    data = JSON[paths[i]]
    # data = data[paths[i+1]]
    print data



find('app.Garden.Flowers.White Flower',j)

【问题讨论】:

    标签: python dictionary


    【解决方案1】:

    这是fold 的一个实例。您可以像这样简洁地编写它:

    from functools import reduce
    import operator
    
    def find(element, json):
        return reduce(operator.getitem, element.split('.'), json)
    

    或者更多 Python 方式(因为 reduce() 由于可读性差而不受欢迎),如下所示:

    def find(element, json):
        keys = element.split('.')
        rv = json
        for key in keys:
            rv = rv[key]
        return rv
    
    j = {"app": {
        "Garden": {
            "Flowers": {
                "Red flower": "Rose",
                "White Flower": "Jasmine",
                "Yellow Flower": "Marigold"
            }
        },
        "Fruits": {
            "Yellow fruit": "Mango",
            "Green fruit": "Guava",
            "White Flower": "groovy"
        },
        "Trees": {
            "label": {
                "Yellow fruit": "Pumpkin",
                "White Flower": "Bogan"
            }
        }
    }}
    print find('app.Garden.Flowers.White Flower', j)
    

    【讨论】:

    • 您可以使用import operatoroperator.getitem,而不是使用 lambda 定义自己的 item-getter 函数。
    • 如果你想通过整数索引 (app.Garden.Flowers.0) 来处理一个项目,你可以像这样改变你的 for 循环:try: rv = rv[int(key)]除了: rv = rv[key] – nr 4 分钟前 编辑
    • 此解决方案不支持列表,如果您将 Flowers 更改为列表 - 会出现错误
    【解决方案2】:

    我遇到了类似的情况,发现了这个dpath module。很好很容易。

    【讨论】:

    • 非常酷。然而,我所希望的是有一种方法可以做类似s = "%('red/buggy/bumpers')s" % { "red": { "buggy": { "bumpers":"foo" }}} 的事情。但我认为使用适当的模板语言比更改字符串上的% 运算符的行为更好。 ?
    • 我的意思是你可以确定以'{'开头并以'}'结尾的子字符串是一个字典。通过 eval() 方法运行该子字符串,该方法将其转换为字典对象。然后拆分您的“/”分隔路径并使用 for 循环应用键
    【解决方案3】:

    您的代码在很大程度上依赖于键名中没有每个出现的点,您可以控制,但不一定。

    我会使用元素名称列表寻找通用解决方案,然后生成列表,例如通过拆分键名的虚线列表:

    class ExtendedDict(dict):
        """changes a normal dict into one where you can hand a list
        as first argument to .get() and it will do a recursive lookup
        result = x.get(['a', 'b', 'c'], default_val)
        """
        def multi_level_get(self, key, default=None):
            if not isinstance(key, list):
                return self.get(key, default)
            # assume that the key is a list of recursively accessible dicts
            def get_one_level(key_list, level, d):
                if level >= len(key_list):
                    if level > len(key_list):
                        raise IndexError
                    return d[key_list[level-1]]
                return get_one_level(key_list, level+1, d[key_list[level-1]])
    
            try:
                return get_one_level(key, 1, self)
            except KeyError:
                return default
    
        get = multi_level_get # if you delete this, you can still use the multi_level-get
    

    一旦你有了这个类,就可以很容易地转换你的 dict 并获得“Jasmine”:

    json = {
            "app": {
                "Garden": {
                    "Flowers": {
                        "Red flower": "Rose",
                        "White Flower": "Jasmine",
                        "Yellow Flower": "Marigold"
                    }
                },
                "Fruits": {
                    "Yellow fruit": "Mango",
                    "Green fruit": "Guava",
                    "White Flower": "groovy"
                },
                "Trees": {
                    "label": {
                        "Yellow fruit": "Pumpkin",
                        "White Flower": "Bogan"
                    }
                }
            }
        }
    
    j = ExtendedDict(json)
    print j.get('app.Garden.Flowers.White Flower'.split('.'))
    

    会得到你:

    Jasmine
    

    与来自字典的普通get() 一样,如果您指定的键(列表)在树中的任何位置都不存在,您将获得None,并且您可以指定第二个参数作为返回值而不是@987654326 @

    【讨论】:

      【解决方案4】:

      我建议你使用python-benedict,它是一个python dict 子类,具有完整的keypath 支持和许多实用方法。

      你只需要转换你现有的字典:

      d = benedict(json)
      # now your keys support dotted keypaths
      print(d['app.Garden.Flower.White Flower'])
      

      这里的库和文档: https://github.com/fabiocaccamo/python-benedict

      注意:我是这个项目的作者

      【讨论】:

        【解决方案5】:

        非常接近。您需要(正如您在评论中所说的那样)递归地遍历主要的 JSON 对象。您可以通过存储最外层键/值的结果,然后使用它来获取下一个键/值等来完成此操作,直到您没有路径。

        def find(element, JSON):     
          paths = element.split(".")
          data = JSON
          for i in range(0,len(paths)):
            data = data[paths[i]]
          print data
        

        您仍然需要注意 KeyErrors。

        【讨论】:

          【解决方案6】:

          单行:

          from functools import reduce
          
          a = {"foo" : { "bar" : "blah" }}
          path = "foo.bar"
          
          reduce(lambda acc,i: acc[i], path.split('.'), a)
          

          【讨论】:

            【解决方案7】:

            选项 1:来自 Cisco 的 pyats 库 [它的 c 扩展]

            • 它又快又超快(如果需要,用 timeit 测量它)
            • Javascript-ish 用法 [括号查找、点查找、组合查找]
            • 点查找缺失键引发属性错误,括号或默认 python dict 查找给出 KeyError。
            pip install pyats pyats-datastructures pyats-utils
            
            from pyats.datastructures import NestedAttrDict
            item = {"specifications": {"os": {"value": "Android"}}}
            path = "specifications.os.value"
            x = NestedAttrDict(item)
            print(x[path])# prints Android
            print(x['specifications'].os.value)# prints Android
            print(x['specifications']['os']['value'])#prints Android
            print(x['specifications'].os.value1)# raises Attribute Error
            

            选项2:pyats.utils chainget

            • 超快(如果需要,用 timeit 测量)
            from pyats.utils import utils
            item = {"specifications": {"os": {"value": "Android"}}}
            path = "specifications.os.value"
            path1 = "specifications.os.value1"
            print(utils.chainget(item,path))# prints android (string version)
            print(utils.chainget(item,path.split('.')))# prints android(array version)
            print(utils.chainget(item,path1))# raises KeyError
            

            选项 3:没有外部库的 python

            1. 与 lambda 相比,速度更快。
            2. 在 lambda 和其他情况下不需要单独的错误处理。
            3. 可读性和简洁性可以是项目中的 utils 函数/助手
            from functools import reduce
            item = {"specifications": {"os": {"value": "Android"}}}
            path1 = "specifications.family.value"
            path2 = "specifications.family.value1"
            
            def test1():
                print(reduce(dict.get, path1.split('.'), item))
            
            def test2():
                print(reduce(dict.get, path2.split('.'), item))
            
            test1() # prints Android
            test2() # prints None
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-11-30
              • 1970-01-01
              • 1970-01-01
              • 2016-04-22
              • 2018-10-08
              • 2018-04-22
              • 1970-01-01
              • 2023-03-15
              相关资源
              最近更新 更多