【问题标题】:Is this the best way to merge two dicts of dicts together and sum similar values这是将两个dicts合并在一起并对相似值求和的最佳方法吗
【发布时间】:2014-01-27 22:38:22
【问题描述】:

是否有更快或更“pythonic”的方式来实现这一点:

dicta = {'a':{'a1':1, 'a2':2}, 'b':{'b1': 1, 'b2': 2}}
dictb = {'b':{'b1':1, 'a2':2}, 'c':{'c1': 1, 'c2': 2}}
dictc = {}

dictc.update(dicta)
for outside_key in dictb:
    if outside_key in dictc:
        for inside_key in dictb[outside_key]:
            if inside_key in dictc[outside_key]:
                dictc[outside_key][inside_key] += dictb[outside_key][inside_key]
            else:
                dictc[outside_key][inside_key] = dictb[outside_key][inside_key]
    else:
        dictc[outside_key] = dictb[outside_key]

dictc 现在包含以下内容:

{'a': {'a1': 1, 'a2': 2},
 'c': {'c2': 2, 'c1': 1},
 'b': {'a2': 2, 'b1': 2, 'b2': 2}}

我在 django 模型定义中使用它,上面似乎是描述问题的最明显方式,但后来我意识到它不够具体。

django 代码如下所示:

def get_capabilities(self):
    capabilities_dict = {}
    capabilities_list = ('card', 'module') #Two ManyToMany fields
    capabilities_dict.update(self.barebone.get_capabilities()) 
    # self.barebone.get_capabilities() is the only foreingkey field
    # and serves as the base on which we build the capabilities list.
    for capability in capabilities_list:
        instances = getattr(self, capability).all()
        for instance in instances:
            capabilities = instance.get_capabilities()
            for capability_name in capabilities:
                if capability_name in capabilities_dict:
                    for obj in capabilities[capability_name]:
                        if obj in capabilities_dict[capability_name]:
                            capabilities_dict[capability_name][obj] += capabilities[capability_name][obj]
                        else:
                            capabilities_dict[capability_name][obj] = capabilities[capability_name][obj]
                else:
                    capabilities_dict[capability_name] = capabilities[capability_name]
    return capabilities_dict

self.barebone.get_capabilities() 看起来像这样:

{'compatible_bus_types': {<BusType: PCI-X>: 1},
 'compatible_storage_interfaces': {<StorageInterface: SATA>: 8},
 'compatible_storage_form_factors': {<StorageFormFactor: 3.5">: 4}}

上面的函数 get_capabilities() 会返回:

{'compatible_network_connectors': {},
 'compatible_storage_interfaces': {<StorageInterface: SATA>: 8,
                                   <StorageInterface: SAS>: 8},
 'compatible_network_standards': {},
 'compatible_storage_form_factors': {<StorageFormFactor: 3.5">: 4},     
 'compatible_bus_types': {<BusType: PCI-X>: 1},
 'compatible_network_form_factors': {}}

每个 封闭的“内部键”实际上是另一个模型实例。

谢谢,

【问题讨论】:

    标签: python django dictionary django-models


    【解决方案1】:

    collections.Counter 与普通字典一起使用:

    >>> from collections import Counter
    >>> dictc = {}
    >>> for d in (dicta, dictb):
        for k, v in d.items():
            dictc[k] = dictc.get(k, Counter()) + Counter(v)
    ...         
    >>> dictc
    {'a': Counter({'a2': 2, 'a1': 1}),
     'c': Counter({'c2': 2, 'c1': 1}),
     'b': Counter({'a2': 2, 'b1': 2, 'b2': 2})}
    

    defaultdict:

    >>> from collections import Counter, defaultdict
    >>> dictc = defaultdict(Counter)
    >>> for d in (dicta, dictb):
        for k, v in d.items():
            dictc[k] += Counter(v)
    ...         
    >>> dictc
    defaultdict(<class 'collections.Counter'>,
    {'a': Counter({'a2': 2, 'a1': 1}),
     'c': Counter({'c2': 2, 'c1': 1}),
     'b': Counter({'a2': 2, 'b1': 2, 'b2': 2})})
    

    【讨论】:

    • 感谢您的回答,这似乎对我最初的担忧非常有益。我更新了我的问题,因为您的回答虽然很好,但不适合我的用例。
    【解决方案2】:

    您使用嵌套的dicts,但我认为有一个更简单的数据结构:您可以使用像('a','a2') 这样的元组 作为键 代替。你的听写应该是这样的:

    dicta = {('a', 'a1'): 1, ('a', 'a2'): 2, ('b', 'b1'): 1, ('b', 'b2'): 2}
    dictb = {('b', 'a2'): 2, ('b', 'b1'): 1, ('c', 'c1'): 1, ('c', 'c2'): 2}
    

    访问值的工作方式如下:

    print dicta['a', 'a1']
    Out: 1
    

    而不是

    print dicta['a']['a1']
    Out: 1
    

    要将您的数据更改为该结构,您可以使用以下功能:

    def unnestdict(dct):
        return { (key1, key2):value 
                             for key1, sub_dct in dct.iteritems()
                                 for key2, value in sub_dct.iteritems()}
    

    如果您使用这种数据结构,您可以轻松地使用来自collections-module 的Counter-class:

    from collections import Counter
    
    counterC = Counter(dicta) + Counter(dictb)
    print counterC
    Out: Counter({('b', 'b2'): 2, ('a', 'a2'): 2, ('b', 'b1'): 2, ('b', 'a2'): 2, ('c', 'c2'): 2, ('a', 'a1'): 1, ('c', 'c1'): 1})
    
    dictc = dict(counterC)
    print dictc
    Out: {('b', 'a2'): 2, ('b', 'b1'): 1, ('c', 'c1'): 1, ('c', 'c2'): 2}
    

    【讨论】:

      猜你喜欢
      • 2017-08-28
      • 2011-10-27
      • 1970-01-01
      • 1970-01-01
      • 2010-12-23
      • 2020-01-25
      • 2018-07-31
      相关资源
      最近更新 更多