【问题标题】:Create a smart function to extract values from list of dictionaries创建一个智能函数以从字典列表中提取值
【发布时间】:2020-03-19 18:41:20
【问题描述】:

我有一个这样的数据列表

data = [
          {
                    "name": "Box 0",
                    "type": "Box",
                    "vals": {
                              "corner1": "0,0,0",
                              "corner2": "0,0,5",
                              "rotate": "0",
                              "scale": "1",
                              "translate": "0,0,0"
                    }
          },
          {
                    "name": "Ovus 1",
                    "type": "Ovus",
                    "vals": {
                              "radiusb": "1",
                              "radiust": "0.5",
                              "rotate": "0",
                              "scale": "1",
                              "translate": "0,0,0"
                    }
          }
]

我想创建一个表单函数

fetch_attributes(name, key)

我可以在其中给出关键属性值类型、vals 或 scale。

我的问题是 scale 属性在 vals 字典中。我可以使函数在我给出 key=type 时给我输入,当我要求 key=scale自动进入vals 字典。

我的方法

我使用此代码作为 name = 'Box 0' 的示例

obj = [i for i in data if i['name'] == name][0]
key_1 = "['vals']" # If I want complete dict of vals
key_2 = "['vals']['scale']" #If I want only one val out of vals
key_3 = "['type']" # If I want type

result_1 = eval('obj' + key_1)
result_2 = eval('obj' + key_2)
result_3 = eval('obj' + key_3)

我想知道是否有更好或更pythonic的方式来完成这项任务

编辑

我想要一种方法,它也将有助于或指导创建一个 edit_attributes 函数

edit_attributes(name, key, value)

【问题讨论】:

  • eval 绝对不是最好的主意。您可以使用dict.get 或将每个查找包装在try...except 中。或者你可以简单地说if key == 'scale': # lookup from vals dict; else: # lookup from obj
  • 实际上除了 vals 之外还有很多子词典,所以使用 try except 块会在列表中的数据发生变化的情况下消除灵活性
  • 这能回答你的问题吗? Python - How to update a multi-dimensional dict

标签: python json python-3.x dictionary


【解决方案1】:

我认为这(现在)可以满足您的要求:

def fetch_attributes(data, name, key):
    for dct in data:
        if dct['name'] == name:
            obj = dct
            break
    else:
        raise KeyError(f'No object with name {name!r} in data')

    if isinstance(obj, dict):
        if key in obj:
            return obj[key]

        for value in obj.values():
            if isinstance(value, dict):  # Nested dict?
                if key in value:
                    return value[key]

    raise KeyError(f'{key!r} not in data')


print(fetch_attributes(data, 'Box 0', 'vals'))   # -> {'corner1': '0,0,0', 'corner2': '0,0,5', 'rotate': '0', 'scale': '1', 'translate': '0,0,0'}
print(fetch_attributes(data, 'Box 0', 'scale'))  # -> 1
print(fetch_attributes(data, 'Box 0', 'type'))   # -> Box

【讨论】:

  • 这是 tkinter UI 的一部分,所以我不能像第二张印刷品那样硬编码 ['scale']。我只能从我的 UI 显示类型、vals、vals(translate)、vals(scale) 等传递密钥。我承认我可以将第二行 ['scale'] 更改为参数,但我试图只使用密钥论据。
  • 我只是删除了if isinstance(obj,dict) 检查,因为在我的情况下它总是正确的。这按预期工作。谢谢
【解决方案2】:

这应该适用于任何级别的字典嵌套。

def find(obj, key):
  if key not in obj:
    for k in obj.keys():
      if type(obj[k]) == type({}):
        return find(obj[k], key)
  else:
    return obj[key]

  return None

def fetch_attributes(name, key):
  obj = [i for i in data if i['name'] == name][0]
  return find(obj, key)

但是,如果obj = [i for i in data if i['name'] == name][0] 返回多个值,那么您还需要一个for 循环。

【讨论】:

  • 第二个循环在你上面的代码中会去哪里?
  • 只有fetch_attributes 函数会改变:def fetch_attributes(name, key): search_results = [] Result = namedtuple('Result', 'index value') obj_list = [i for i in data if i['name'] == name] for obj in obj_list: value = find(obj, key) res = Result(obj_list.index(obj), value) search_results.append(res) return search_results 所以我们需要遍历obj_list 并为每个匹配运行findnamedtuple Result 保存每个字典的匹配键值(由其在obj_list 中的索引指示)。
【解决方案3】:

当找不到属性名称时,您可以利用字典的 get() 函数的第二个参数级联到子字典:

def getProp(d,key): return d.get(key,d["vals"].get(key,None))

输出:

for d in data:
    print([ getProp(d,"name"),getProp(d,"type"),getProp(d,"scale") ])

# ['Box 0', 'Box', '1']
# ['Ovus 1', 'Ovus', '1']

赋值会稍微复杂一些,但也可以使用 setdefault():

def setProp(d,key,value):
    [d,d.setdefault("vals",dict())][key=="vals"][key] = value

【讨论】:

    猜你喜欢
    • 2023-01-17
    • 2022-11-30
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 2020-08-31
    • 1970-01-01
    • 1970-01-01
    • 2021-01-26
    相关资源
    最近更新 更多