来自键路径的嵌套字典值

时间:2015-06-24 17:49:54

标签: python dictionary

借助键路径从嵌套字典中获取值,这里是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)

7 个答案:

答案 0 :(得分:25)

这是fold的一个实例。您可以这样简洁地写出来:

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)

答案 1 :(得分:4)

派对有点晚了,但我遇到了类似的情况并发现了dpath module。很好,很容易。

希望这有助于其他人:)

答案 2 :(得分:2)

您的代码在很大程度上取决于您可能能够控制的键名称中不会出现任何点,但不一定。

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

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

一旦你上了这门课,很容易改变你的词典并得到" 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

与dict中的普通get()一样,如果您指定的键(列表)不存在于树中的任何位置,则会得到None,并且您可以将第二个参数指定为返回值而不是None

答案 3 :(得分:2)

单线:

from functools import reduce

a = {"foo" : { "bar" : "blah" }}
path = "foo.bar"

reduce(lambda acc,i: acc[i], path.split('.'), a)

答案 4 :(得分:1)

非常接近。你需要(正如你在评论中所做的那样)递归地浏览主JSON对象。您可以通过存储最外面的键/值的结果,然后使用它来获取下一个键/值等来实现这一点,直到您没有路径。

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

你仍然需要注意KeyErrors。

答案 5 :(得分:1)

我建议您使用python-benedict,它是具有完整键路径支持和许多实用程序方法的python dict子类。

您只需要投射现有的字典:

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

以下是库和文档: https://github.com/fabiocaccamo/python-benedict

答案 6 :(得分:1)

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

  • 它的快速和超快(如果需要,用 timeit 测量它)
  • Javascript-ish 用法[括号查找,点查找,组合查找]
  • 缺少键的点式查找会引发属性错误,括号或默认的 Python 字典查找会导致 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