2013-07-14 30 views
2

假設您有幾個字典可以記錄每個鍵的三個浮點值(在子字典中)。您希望能夠以添加多個字段中存在的密鑰值的方式來合併這些字典。Python:爲什麼每個按鍵添加字典值取決於順序?

對於普通的字典更新,值將被覆蓋,所以你繼承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] 

唉,添加指令的順序確實會影響結果。但是,爲什麼,以及意外結果發生了什麼?

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} 

添加類型的字典中二階似乎加倍值(增加了他們兩次?),這是第一次放在字典的。我不明白爲什麼會發生這種情況。我如何獲得所需的附加行爲可靠發生,與添加順序無關?

另一件我不明白的事情:爲什麼small_test1a的值會在我製作新的StatementDict()時發生變化並填充相同的值?

運行下面的線引起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 

P.S.使用我的實際數據,根本不會發生任何增加。相反,保留了第一個放置的值。這是而不是與更新普通字典相同,其中值被覆蓋。不幸的是,我無法用小的測試數據重現這種行爲。

回答

1

當你這樣做:

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 ... 
+0

謝謝你提醒我複製!我之前多次用字典犯了這個錯誤,所以我現在對自己有點失望了。 然而,使用'collections.defaultdict'似乎不那麼簡單。我試圖將其子類化爲'StatementDict(defaultdict(lvl_dict_factory))',其中'lvl_dict_factory = lambda:{'skill':0.0,'knowledge':0.0,'interest':0.0}',但這顯然不是它的完成方式。 – AliOli

+0

啊,這是調用它的錯誤方法。如果你有'class StatementDict(defaultdict)',你可以用例如'small_test1a = StatementDict(lvl_dict_factory)'創建一個實例。或者,提供你自己的'__init__',它可能會讓你看起來更清晰;我將在一個示例中進行編輯。 – torek