【问题标题】:Modifying keys and values in nested dictionaries in python在python中修改嵌套字典中的键和值
【发布时间】:2018-07-10 01:43:58
【问题描述】:

我正在使用嵌套字典,并试图弄清楚如何有效地修改某些嵌套和非嵌套键/值。简而言之,我正在尝试执行以下操作:

  1. 获取嵌套值并使用非嵌套键切换它
  2. 重命名嵌套键。

这是我正在使用的字典的基本示例:

pet_dictionary = {'Buford':{'color':'white', 'weight': 95, 'age':'3', 
                  'breed':'bulldog'}, 
                  'Henley':{'color':'blue', 'weight': 70, 'age':'2', 
                  'breed':'bulldog'}, 
                  'Emi':{'color':'lilac', 'weight': 65, 'age':'1', 
                  'breed':'bulldog'}, 
                  }

我想取非嵌套键,即每只狗的名字(即 Buford、Henley、Emi),用年龄的嵌套值(即 3、2、1)切换它,然后更改嵌套从“年龄”到“姓名”的键名。所以输出应该是这样的:

pet_dictionary = {'3':{'color':'white', 'weight': 95, 'name':'Buford', 
                  'breed':'bulldog'}, 
                  '2':{'color':'blue', 'weight': 70, 'name':'Henley', 
                  'breed':'bulldog'}, 
                  '1':{'color':'lilac', 'weight': 65, 'name':'Emi', 
                  'breed':'bulldog'}, 
                  }

我了解如何逐一手动执行此操作,但我不确定以更优雅/最佳方式进行所有这些更改的最佳方法是什么。

【问题讨论】:

  • 请注意,从概念上讲,您不能更改字典中的键:如果您完全修改原始键,则必须删除旧的键值关联并添加一个新的(具有相同的值)。

标签: python dictionary nested


【解决方案1】:

在迭代字典时,您可以通过三个步骤干净地构建一个新字典:

# Preserves original dict
d = {}
for k, v in pet_dictionary.items():                     
    key = v["age"]                                                 # 1. grab the new key
    d[key] = {"name": k}                                           # 2. add new "name" item
    d[key].update({k_:v_ for k_, v_ in v.items() if k_!="age"})    # 3. update the new dict

d

【讨论】:

    【解决方案2】:

    这可能会有所帮助

    pet_dictionary = {'Buford':{'color':'white', 'weight': 95, 'age':'3',
                      'breed':'bulldog'},
                      'Henley':{'color':'blue', 'weight': 70, 'age':'2',
                      'breed':'bulldog'},
                      'Emi':{'color':'lilac', 'weight': 65, 'age':'1',
                      'breed':'bulldog'},
                      }
    
    d = {}
    for k,v in pet_dictionary.items():
        d[v['age']] = pet_dictionary[k]
        d[v['age']].update({"name": k})
        del d[v['age']]['age']
    
    print d
    

    输出

    {'1': {'color': 'lilac', 'breed': 'bulldog', 'name': 'Emi', 'weight': 65}, '3': {'color': 'white', 'breed': 'bulldog', 'name': 'Buford', 'weight': 95}, '2': {'color': 'blue', 'breed': 'bulldog', 'name': 'Henley', 'weight': 70}}
    

    【讨论】:

    • 我认为你可以用d[v["age"]]缩短d[pet_dictionary[k]['age']]
    【解决方案3】:

    这是 python 可迭代对象大放异彩的情况

    p = {'Buford':{'color':'white', 'weight': 95, 'age':'3',
                      'breed':'bulldog'},
                      'Henley':{'color':'blue', 'weight': 70, 'age':'2',
                      'breed':'bulldog'},
                      'Emi':{'color':'lilac', 'weight': 65, 'age':'1',
                      'breed':'bulldog'},
                      }
    
    new_dictionary = {p[i]['age']:{'color':p[i]['color'],'weight':p[i]['weight'],
                        'name':i,'breed':p[i]['breed']} for i in p}
    

    输出:

    {'3': {'color': 'white', 'weight': 95, 'name': 'Buford', 'breed': 'bulldog'},
    '2': {'color': 'blue', 'weight': 70, 'name': 'Henley', 'breed': 'bulldog'},
    '1': {'color': 'lilac', 'weight': 65, 'name': 'Emi', 'breed': 'bulldog'}}
    

    【讨论】:

    • 你提到了迭代器。你具体指的是什么?
    • Python 字典是可迭代的,这意味着你可以像我上面那样使用 {i for i in dict} 语法!
    • 是的。您正在使用可迭代的字典理解,而不是 iterator。这些很容易混淆。这是一个快速测试:迭代器必须可用于iter()next()。如果您尝试next(new_dictionary),您将看到TypeError: 'dict' object is not an iterator。所以你必须编辑你的第一行。否则,干得好。
    【解决方案4】:

    通过几个推导,您可以进行如下转换:

    代码:

    new_dict = {
        info['age']: {k: v for k, v in list(info.items()) + [('name', name)]
                      if k != 'age'}
        for name, info in pet_dictionary.items()
    }
    

    测试代码:

    pet_dictionary = {
        'Buford': {'color': 'white', 'weight': 95, 'age': '3', 'breed': 'bulldog'},
        'Henley': {'color': 'blue', 'weight': 70, 'age': '2', 'breed': 'bulldog'},
        'Emi': {'color': 'lilac', 'weight': 65, 'age': '1', 'breed': 'bulldog'},
    }
    
    new_dict = {
        info['age']: {k: v for k, v in list(info.items()) + [('name', name)]
                      if k != 'age'}
        for name, info in pet_dictionary.items()
    }
    
    for dog in new_dict.items():
        print(dog)
    

    结果:

    ('3', {'color': 'white', 'weight': 95, 'breed': 'bulldog', 'name': 'Buford'})
    ('2', {'color': 'blue', 'weight': 70, 'breed': 'bulldog', 'name': 'Henley'})
    ('1', {'color': 'lilac', 'weight': 65, 'breed': 'bulldog', 'name': 'Emi'})
    

    【讨论】:

      【解决方案5】:

      pandas

      import pandas as pd
      
      pet_dictionary = {'Buford':{'color':'white', 'weight': 95, 'age':'3', 
                      'breed':'bulldog'}, 
                      'Henley':{'color':'blue', 'weight': 70, 'age':'2', 
                      'breed':'bulldog'}, 
                      'Emi':{'color':'lilac', 'weight': 65, 'age':'1', 
                      'breed':'bulldog'}, 
                      }
      pd.DataFrame.from_dict(pet_dictionary, orient='index') \
        .reset_index() \
        .rename(columns={'index': 'name'}) \
        .set_index('age') \
        .to_dict('index')
      

      【讨论】:

        【解决方案6】:
        def flip(k, v):
           v1 = dict(v)
           v1.update(name=k)
           return v1.pop('age'), v1
        
        pet_dictionary2 = dict([flip(k, v) for k, v in pet_dictionary.items()])
        
        
        # import pprint as pp; pp.pprint(pet_dictionary2)
        # {'1': {'breed': 'bulldog', 'color': 'lilac', 'name': 'Emi', 'weight': 65},
        #  '2': {'breed': 'bulldog', 'color': 'blue', 'name': 'Henley', 'weight': 70},
        #  '3': {'breed': 'bulldog', 'color': 'white', 'name': 'Buford', 'weight': 95}}
        

        如果可以更改以前的字典,那么你可以这样做:

        def flip(k, v):
            v.update(name=k)
            return v.pop('age'), v
        

        【讨论】:

          【解决方案7】:

          只需几行代码,无需任何额外的库,但改变原始字典:

          pet_dictionary = {
              nested.pop('age'): nested.update({'name': name}) or nested 
              for name, nested in pet_dictionary.items()
           }
          

          还有额外的导入,但没有变异 pet_dictionary:

          import copy
          
          new_pet_dict = {
              nested.pop('age'): nested.update({'name': name}) or nested 
              for name, nested in copy.deepcopy(pet_dictionary).items()
          }
          

          ...保留原始pet_dictionary 不变。

          信息

          最初,我发布了不同的答案,其中使用.pop 方法创建的新字典中的键和使用{**nested, 'name': name} 的嵌套字典但它不起作用。这将是更清洁的解决方案,但是AFAIK,解释器从右到左读取代码并且......这显然无法使用这种方法。

          那么这是如何工作的呢?它看起来有点棘手,尤其是行:

          nested.update({'name': name}) or nested
          

          但是让我们仔细看看。我们需要用 name 键更新嵌套,但这会返回 None 并改变对象。所以这个or 的左边部分将始终是None,我们希望在我们的dict 理解中有dict 对象。这是 Python 中的short circuit evaluation,如果第一个操作数是假的,它总是返回第二个操作数。

          非变异示例使用 deepcopy 并变异临时副本,而不是原始字典。

          【讨论】:

            【解决方案8】:

            Python 3.8 更新:

            只有在可以接受改变原始字典的情况下(感谢@pylang 注意到它),使用字典的语法很简洁:

            new = {nested.pop('age'): {**nested, 'name': name} for name, nested in pet_dictionary.items()}
            

            【讨论】:

            • 这就是这样做的方法。做得好。如果可以接受的话,唯一的问题是改变原始字典。
            • 一种非破坏性的方法可能是使用另一种理解new = {nested['age']: {**{k:v for k,v in nested if k!="age"} for name, nested in pet_dictionary.items()}
            • 好吧,我有时更喜欢像你这样的具有显式循环的解决方案更清晰。是的,只有在不再使用以前的 dict 时才可以接受。如果是这样,我将使用 for 循环为您提供初始解决方案,因为嵌套理解的可读性要差得多。
            【解决方案9】:

            这里有两个班轮:

            ##add new value, which was formerly the key
            {k: v.update({'name':k}) for k,v in pet_dictionary.items()}
            ##reset keys to value of 'age'
            new_pet = {v.get('age'): v for k,v in pet_dictionary.items()}
            

            【讨论】:

            • 首先:突变v 对您的第一次理解没有好处,这会无缘无故地创建带有None 值的dict。第二:age 仍然是您的 new_pet 字典中的关键(您可以使用 pop 而不是 get 来修复它)。
            猜你喜欢
            • 2019-07-18
            • 1970-01-01
            • 1970-01-01
            • 2018-09-13
            • 1970-01-01
            • 2021-04-10
            • 1970-01-01
            • 2019-07-07
            • 1970-01-01
            相关资源
            最近更新 更多