【问题标题】:Looping through nested dict only returns last value unless I assign it to another dict遍历嵌套的字典只返回最后一个值,除非我将它分配给另一个字典
【发布时间】:2015-11-09 03:01:31
【问题描述】:

我有一个以下结构的字典 products

{'Cheese': {'Cheese': {'comment': 'Markets quiet this week', 'indicator': 'Flat', 'pricelow': '3385', 'priceaverage': '3385', 'pricehigh': '3385'}}, 'MPC': {'MPC70': {'comment': 'Large buyers', 'indicator': 'Flat', 'pricelow': '8598', 'priceaverage': '8929', 'pricehigh': '9039'}, 'MPI:': {'comment': 'Large buyers', 'indicator': 'Flat', 'pricelow': '8598', 'priceaverage': '8929', 'pricehigh': '9039'}, 'MPC85': {'comment': 'Large buyers', 'indicator': 'Flat', 'pricelow': '8598', 'priceaverage': '8929', 'pricehigh': '9039'}}}

我想解决嵌套结构,为此我想取产品名称“Cheese”,“MPC70”,“MPI”,“MPC85”,将其添加到内部字典中作为“名称”并把所有它在一个列表中。 (类别(“Cheese”和“MPC”不再重要,将会丢失)有这样的东西:

[{'indicator': 'Flat', 
 'comment': 'Markets quiet this week', 
 'name': 'Cheese', 
 'pricelow': '3385', 
 'priceaverage': '3385', 
 'pricehigh': '3385'}, 
{'indicator': 'Flat', 
 'comment': 'Large buyers.High Ground.', 
 'name': 'MPC70', 
 'pricelow': '4189', 
 'priceaverage': '4409', 
 'pricehigh': '5070'}, 
{'indicator': 'Flat', 
 'comment': 'Large buyers.High Ground.', 
 'name': 'MPI:', 
 'pricelow': '8598', 
 'priceaverage': '8929', 
 'pricehigh': '9039'}, 
{'indicator': 'Flat', 
 'comment': 'Large buyers.High Ground.', 
 'name': 'MPC85', 
 'pricelow': '8377', 
 'priceaverage': '8488', 
 'pricehigh': '8818'}
]

到目前为止一切都很好,但是当我遍历“MPC”类别中所有产品的字典时,名称是“MPC85” 我的循环如下所示:

for item in products:
  for subitem in products[item]:
    products[item][subitem]['name'] = subitem
    productlist.append(products[item][subitem])
    #print products[item][subitem]
    #print products[item][subitem]['name']
    #print  item + ' ' + subitem
print productlist

对于任何带注释的打印语句,输出完全符合我的要求,但是如果我打印整个列表,则每个“MPC”产品的“名称”键是“MPC85”\

经过反复试验并确认@spectras 循环确实有效,我想如果我用

将 dict 分配给另一个变量
products2 = products

并将循环更改为循环 products2 而不是 products 整个事情都有效。

所以问题是:为什么 python 在循环一个 dict 时似乎有问题,而当我将 dict 分配给另一个变量时它却按预期执行?

编辑: 我所做的和我得到的完整转储:

In [3]: print products
{'Cheese': {'Cheese': {'comment': 'Markets quiet this week', 'indicator': 'Flat', 'pricelow': '3385', 'priceaverage': '3385', 'pricehigh': '3385'}}, 'MPC': {'MPC70': {'comment': 'Large buyers.High Ground.', 'indicator': 'Flat', 'pricelow': '8598', 'priceaverage': '8929', 'pricehigh': '9039'}, 'MPI:': {'comment': 'Large buyers.High Ground.', 'indicator': 'Flat', 'pricelow': '8598', 'priceaverage': '8929', 'pricehigh': '9039'}, 'MPC85': {'comment': 'Large buyers.High Ground.', 'indicator': 'Flat', 'pricelow': '8598', 'priceaverage': '8929', 'pricehigh': '9039'}}, 'Whey Powder': {'Whey Powder': {'comment': 'Dry Whey futures drop', 'indicator': 'Down', 'pricelow': '1124', 'priceaverage': '1124', 'pricehigh': '1124'}}, 'Casein': {'Casein': {'comment': 'No changes.', 'indicator': 'Down', 'pricelow': '7165', 'priceaverage': '7605', 'pricehigh': '8157'}}, 'Lactose': {'Lactose': {'comment': 'Lactose prices remain unchanged', 'indicator': 'Down', 'pricelow': '683', 'priceaverage': '683', 'pricehigh': '683'}}, 'Powders': {'NFDM': {'comment': 'Export volumes dropped.', 'indicator': 'Up', 'pricelow': '2408', 'priceaverage': '2408', 'pricehigh': '2408'}}, 'Caseinate': {'Caseinate': {'comment': 'actively selling.requested pricing.', 'indicator': 'Down', 'pricelow': '7716', 'priceaverage': '8598', 'pricehigh': '9479'}}, 'Cream': {'Butter': {'comment': 'Butter stocks increased', 'indicator': 'Down', 'pricelow': '3847', 'priceaverage': '3847', 'pricehigh': '3847'}}, 'WPC': {'WPC80': {'comment': 'WPC 80 \xe2\x80\x93 Supply continues.', 'indicator': 'Down', 'pricelow': '5300', 'priceaverage': '7165', 'pricehigh': '8267'}, 'IWPC80': {'comment': 'WPC 80 \xe2\x80\x93 Supply continues.', 'indicator': 'Down', 'pricelow': '5300', 'priceaverage': '7165', 'pricehigh': '8267'}}}

In [4]: %paste
        for item in products:
                for subitem in products[item]:
                        products[item][subitem]['name'] = subitem
                        #print products[item][subitem]
                        #print products[item][subitem]['name']
                        productlist.append(products[item][subitem])
                        #print  item + ' ' + subitem
                        #print products[item][subitem]['name']
                        #print products[item]
                        #print products[item][subitem]
        print productlist

## -- End pasted text --
[{'comment': 'Markets quiet this week', 'indicator': 'Flat', 'name': 'Cheese', 'pricelow': '3385', 'priceaverage': '3385', 'pricehigh': '3385'}, {'comment': 'Large buyers.High Ground.', 'indicator': 'Flat', 'name': 'MPC85', 'pricelow': '8598', 'priceaverage': '8929', 'pricehigh': '9039'}, {'comment': 'Large buyers.High Ground.', 'indicator': 'Flat', 'name': 'MPC85', 'pricelow': '8598', 'priceaverage': '8929', 'pricehigh': '9039'}, {'comment': 'Large buyers.High Ground.', 'indicator': 'Flat', 'name': 'MPC85', 'pricelow': '8598', 'priceaverage': '8929', 'pricehigh': '9039'}, {'comment': 'Dry Whey futures drop', 'indicator': 'Down', 'name': 'Whey Powder', 'pricelow': '1124', 'priceaverage': '1124', 'pricehigh': '1124'}, {'comment': 'No changes.', 'indicator': 'Down', 'name': 'Casein', 'pricelow': '7165', 'priceaverage': '7605', 'pricehigh': '8157'}, {'comment': 'Lactose prices remain unchanged', 'indicator': 'Down', 'name': 'Lactose', 'pricelow': '683', 'priceaverage': '683', 'pricehigh': '683'}, {'comment': 'Export volumes dropped.', 'indicator': 'Up', 'name': 'NFDM', 'pricelow': '2408', 'priceaverage': '2408', 'pricehigh': '2408'}, {'comment': 'actively selling.requested pricing.', 'indicator': 'Down', 'name': 'Caseinate', 'pricelow': '7716', 'priceaverage': '8598', 'pricehigh': '9479'}, {'comment': 'Butter stocks increased', 'indicator': 'Down', 'name': 'Butter', 'pricelow': '3847', 'priceaverage': '3847', 'pricehigh': '3847'}, {'comment': 'WPC 80 \xe2\x80\x93 Supply continues.', 'indicator': 'Down', 'name': 'IWPC80', 'pricelow': '5300', 'priceaverage': '7165', 'pricehigh': '8267'}, {'comment': 'WPC 80 \xe2\x80\x93 Supply continues.', 'indicator': 'Down', 'name': 'IWPC80', 'pricelow': '5300', 'priceaverage': '7165', 'pricehigh': '8267'}]

In [5]: type(products)
Out[5]: dict

In [6]: type(products['Cheese']
   ...: )
Out[6]: dict

编辑2: 冉建议从 cmets 截取,输出:

    In [3]: print products
{'Cheese': {'Cheese': {'comment': 'Markets quiet this week', 'indicator': 'Flat', 'pricelow': '3385', 'priceaverage': '3385', 'pricehigh': '3385'}}, 'MPC': {'MPC70': {'comment': 'Large buyers.High Ground.', 'indicator': 'Flat', 'pricelow': '8598', 'priceaverage': '8929', 'pricehigh': '9039'}, 'MPI:': {'comment': 'Large buyers.High Ground.', 'indicator': 'Flat', 'pricelow': '8598', 'priceaverage': '8929', 'pricehigh': '9039'}, 'MPC85': {'comment': 'Large buyers.High Ground.', 'indicator': 'Flat', 'pricelow': '8598', 'priceaverage': '8929', 'pricehigh': '9039'}}, 'Whey Powder': {'Whey Powder': {'comment': 'Dry Whey futures drop', 'indicator': 'Down', 'pricelow': '1124', 'priceaverage': '1124', 'pricehigh': '1124'}}, 'Casein': {'Casein': {'comment': 'No changes.', 'indicator': 'Down', 'pricelow': '7165', 'priceaverage': '7605', 'pricehigh': '8157'}}, 'Lactose': {'Lactose': {'comment': 'Lactose prices remain unchanged', 'indicator': 'Down', 'pricelow': '683', 'priceaverage': '683', 'pricehigh': '683'}}, 'Powders': {'NFDM': {'comment': 'Export volumes dropped.', 'indicator': 'Up', 'pricelow': '2408', 'priceaverage': '2408', 'pricehigh': '2408'}}, 'Caseinate': {'Caseinate': {'comment': 'actively selling.requested pricing.', 'indicator': 'Down', 'pricelow': '7716', 'priceaverage': '8598', 'pricehigh': '9479'}}, 'Cream': {'Butter': {'comment': 'Butter stocks increased', 'indicator': 'Down', 'pricelow': '3847', 'priceaverage': '3847', 'pricehigh': '3847'}}, 'WPC': {'WPC80': {'comment': 'WPC 80 \xe2\x80\x93 Supply continues.', 'indicator': 'Down', 'pricelow': '5300', 'priceaverage': '7165', 'pricehigh': '8267'}, 'IWPC80': {'comment': 'WPC 80 \xe2\x80\x93 Supply continues.', 'indicator': 'Down', 'pricelow': '5300', 'priceaverage': '7165', 'pricehigh': '8267'}}}

In [4]: %paste
from pprint import pprint; from itertools import chain; pprint([(key, type(val).__name__, id(val)) for key, val in chain.from_iterable(x.items() for x in products.values())])

## -- End pasted text --
[('Cheese', 'dict', 64885352L),
 ('MPC70', 'dict', 64887800L),
 ('MPI:', 'dict', 64887800L),
 ('MPC85', 'dict', 64887800L),
 ('Whey Powder', 'dict', 65061480L),
 ('Casein', 'dict', 65061752L),
 ('Lactose', 'dict', 65062024L),
 ('NFDM', 'dict', 65062296L),
 ('Caseinate', 'dict', 65062568L),
 ('Butter', 'dict', 65063112L),
 ('WPC80', 'dict', 65062840L),
 ('IWPC80', 'dict', 65062840L)]

【问题讨论】:

  • 您发布的代码确实有效。您可能没有准确发布您所拥有的内容。
  • 我刚刚又试了一次,检查了我的dict是否看起来像我展示并使用了我在这里发布的代码,问题仍然存在。
  • @亚历山大。再检查一遍。您问题中的示例不是有效的字典 - 但如果它已修复,则循环按预期工作。复制并粘贴您的真实代码,而不是您认为看起来像的代码。
  • 您在 python 文件中发布的代码:gist.github.com/spectras/0f50994c7086fb139567 在 python2 和 python3 上完美运行。
  • 我添加了真正的字典。 @spectras 我可以确认您的代码有效,但它不适用于我的 dict。所以我看起来那里有一个错误。

标签: python python-2.7 loops dictionary


【解决方案1】:

好的,问题现在很清楚了。

我让你复制粘贴的示例行的解释:

from pprint import pprint
from itertools import chain
pprint([(key, type(val).__name__, id(val))
        for key, val in chain.from_iterable(x.items() for x in products.values())])

这会遍历每个 2 级字典并打印:

  1. 用于访问它的密钥。
  2. 字典的类型(以确保它实际上是字典)
  3. 字典的唯一标识符。

因此,鉴于这些结果,有一些重要的事情需要注意:

   [('Cheese', 'dict', 64885352L),
     ('MPC70', 'dict', 64887800L),
     ('MPI:', 'dict', 64887800L),
     ('MPC85', 'dict', 64887800L),
     ('Whey Powder', 'dict', 65061480L),
     ('Casein', 'dict', 65061752L),
     ('Lactose', 'dict', 65062024L),
     ('NFDM', 'dict', 65062296L),
     ('Caseinate', 'dict', 65062568L),
     ('Butter', 'dict', 65063112L),
     ('WPC80', 'dict', 65062840L),
     ('IWPC80', 'dict', 65062840L)]

→ 您的某些产品共享相同的字典。看看 MPC70、MPI 和 MPC85 如何具有相同的唯一标识符?

因此,不是添加三个不同的字典,每个字典都添加了 name 到您的产品列表中,而是您最终添加了三次相同的字典,每次循环迭代都会覆盖 name

这也解释了为什么复制粘贴会改变问题。复制粘贴时,您创建了三个不同的字典,它们恰好看起来相同。

我们现在可以修复您的代码,知道它必须明确地复制产品,而不是简单地重复使用它们:

for item in products:
  for subitem in products[item]:
    product = products[item][subitem].copy()
    product['name'] = subitem
    productlist.append(product)

作为奖励,相同的循环编写效率更高:

for item in products.values():
  for subkey, subitem in item.items():
    product = subitem.copy()
    product['name'] = subkey
    productlist.append(product)

【讨论】:

  • 太好了,更优雅地解决了问题。有没有办法从源头解决它,即。以不同的方式创建字典,我不会再次遇到同样的问题?我使用嵌套的 for 循环和 products[product][productname] = {} 以及最内层循环中的赋值 products[product][productname]['indicator'] = indicator 创建它们
  • 嗯,这需要整个循环代码才能确定。可能您在外部循环中创建字典并在内部循环中分配它,因此您最终为每个子项分配相同的字典。请记住,在python中,当您执行x = y时,您不会复制y,您只需复制引用,因此xy指向同一个对象。当您需要实际副本时,您必须明确地执行此操作。例如,x = list(y)(用于列表)或x = y.copy()(用于字典)。
  • 我会记住这一点。据我了解,我在每个循环中都指的是一个新的字典;但如果它适用于您的循环,那么对于这个项目来说已经足够了。谢谢
猜你喜欢
  • 1970-01-01
  • 2019-02-15
  • 2021-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-30
  • 2020-12-07
相关资源
最近更新 更多