Skip to main content
 首页 » 编程设计

Python:为什么每个键添加字典值取决于顺序

2025年05月04日9davidwang456

假设您有多个字典,每个键(在子字典中)跟踪三个浮点值。您希望能够以添加存在于多个字典中的键值的方式合并这些字典。

通过普通的字典更新,值会被覆盖,所以你将 dict() 子类化:

class StatementDict(dict): 
    def add(self, statement): 
        ann_id = statement[0] 
        lvl_dict = statement[1] 
        if ann_id in self: 
            self[ann_id]['skill'] += lvl_dict['skill'] 
            self[ann_id]['knowledge'] += lvl_dict['knowledge'] 
            self[ann_id]['interest'] += lvl_dict['interest'] 
        else: 
            self[ann_id] = lvl_dict 
 
    def update(self, statement_dict): 
        for statement in statement_dict.iteritems(): 
            self.add(statement) 

然后将要合并/添加的字典放入普通字典的键中:

# Small example data that reproduces the error 
few_statements = {} 
few_statements['linkedin'] = {u'Homerun': {u'skill': 14.0, 
                                           u'knowledge': 34.0, 
                                           u'interest': 20.0}} 
few_statements['tudelft'] = {u'Presentation': {u'skill': 14.0, 
                                               u'knowledge': 34.0, 
                                               u'interest': 20.0}, 
                             u'Future': {u'skill': 16.0, 
                                         u'knowledge': 25.33, 
                                         u'interest': 2.0}, 
                             u'Visual_perception': {u'skill': 20.46, 
                                                    u'knowledge': 28.35, 
                                                    u'interest': 4.0}} 
few_statements['website'] = {u'Homerun': {u'skill': 1.0, 
                                          u'knowledge': 3.0, 
                                          u'interest': 2.0}} 
 
few_statements['shareworks'] = {u'Presentation': {u'skill': 8.0, 
                                                  u'knowledge': 20.0, 
                                                  u'interest': 12.0}, 
                                u'Future': {u'skill': 17.0, 
                                            u'knowledge': 26.33, 
                                            u'interest': 3.0}, 
                                u'Visual_perception': {u'skill': 2.0, 
                                                       u'knowledge': 3.0, 
                                                       u'interest': 6.0}} 

现在我们应该能够将这些键值对逐个添加到 StatementDict() 中,或者使用 StatementDict.update() 方法。源字典添加到 StatementDict 的顺序对结果没有影响。

# First we try updating in one order 
small_test1a = StatementDict() 
for origin in ("tudelft", "website", "linkedin", "shareworks"): 
    for st in few_statements[origin].iteritems(): 
        small_test1a.add(st) 
 
# And then in another order 
small_test2 = StatementDict() 
for origin in ("linkedin", "shareworks", "tudelft", "website"): 
    for st in few_statements[origin].iteritems(): 
        small_test2.add(st) 
 
print "Different order, same result?", small_test1a == small_test2 
                                                # False, but why? 
for key in small_test1a: 
    print "Desired:", key, small_test1a[key] 
    print "Unexpected:", key, small_test2[key] 

唉,dict 添加的顺序确实会影响结果。但是为什么会发生意外结果呢?

Desired: Future {u'skill': 33.0, u'knowledge': 51.66, u'interest': 5.0} 
Unexpected: Future {u'skill': 50.0, u'knowledge': 77.99, u'interest': 8.0} 
Desired: Presentation {u'skill': 22.0, u'knowledge': 54.0, u'interest': 32.0} 
Unexpected: Presentation {u'skill': 30.0, u'knowledge': 74.0, u'interest': 44.0} 
Desired: Homerun {u'skill': 15.0, u'knowledge': 37.0, u'interest': 22.0} 
Unexpected: Homerun {u'skill': 29.0, u'knowledge': 71.0, u'interest': 42.0} 
Desired: Visual_perception {u'skill': 22.46, u'knowledge': 31.35, u'interest': 10.0} 
Unexpected: Visual_perception {u'skill': 24.46, u'knowledge': 34.35, u'interest': 16.0} 

以第二顺序添加字典似乎会使首先放置的字典的值加倍(将它们添加两次?)。我不明白为什么会这样。如何独立于添加顺序可靠地发生所需的添加行为?

还有一件事我不明白:当我创建一个新的 StatementDict() 并用相同的值填充它时,为什么 small_test1a 的值会改变?

运行以下行会导致 small_test1a 在循环的最后一次迭代中发生变化:

small_test1b = StatementDict() 
for origin in ("tudelft", "website", "linkedin", "shareworks"): 
    small_test1b.update(few_statements[origin]) 
print "\nDoes .update() function?", small_test1a == small_test1b 
print small_test1a 

附言使用我的实际数据,根本没有添加。相反,将保留第一个放置的值。这与更新普通字典不同,后者的值被覆盖。不幸的是,我无法使用少量测试数据重现此行为。

请您参考如下方法:

当你这样做时:

self[ann_id] = lvl_dict 

您将 self[ann_id] 设为该特定词典的另一个名称(例如,“tudelft”的名称)。然后,当您执行后续操作时:

self[ann_id]['skill'] += lvl_dict['skill'] 

您根据当前版本修改先前的 lvl_dict(即,在本例中,根据“网站”更改“tudelft”的版本)。

对此的最小修复是 copy第一本词典。但是,我可能会尝试使用 collections.defaultdict,这样您就可以完全消除 if ann_id in self: 测试。当 defaultdict 创建新字典时,它将是一个新实例,因此不会修改任何现有字典。


在下面的评论中使用 defaultdict 和 lambda 函数的示例:

from collections import defaultdict 
 
class StatementDict(defaultdict): 
    def __init__(self): 
        defaultdict.__init__(self, 
            lambda: {'skill': 0.0, 'knowledge': 0.0, 'interest': 0.0}) 
 
    def add(self, statement): 
        ... as before ...