2016-08-05 81 views
1

我不明白這一點,它會打擾我,直到我這樣做。python字典中返回值的隨機順序

這Python代碼計算每個字符出現在「消息」變量的次數:

message = 'Some random string of words' 

dictionary= {} 

for character in message.upper(): 
    dictionary.setdefault(character,0) 
    dictionary[character] = dictionary[character] + 1 

print(dictionary) 

如果你運行這個多次,你會發現數以看似隨機的順序每次返回。爲什麼是這樣?我會認爲循環應該每次從字符串的開始處開始,並以一致的順序返回值......但它們不會。影響字符串處理順序的setdefault(),print()upper()方法中是否存在一些隨機性元素?

+0

詞典是鍵值對** set **。不是一個列表。一套。並且集合沒有順序。 – SuperSaiyan

+0

http://stackoverflow.com/questions/1867861/python-dictionary-keep-keys-values-in-same-order-as-declared – Abdou

+0

@SuperSaiyan - 謝謝你的反饋。我明白字典不是命令的,我更想理解爲什麼。對我來說,相似的直覺告訴我,相同的基本代碼會以隨機順序返回值......我對這種情況的內部情況感到好奇。 – DCaugs

回答

3

因爲兩件事情:

  • 字典 「是沒有順序的。」您當然可以獲得一些順序,但它取決於密鑰的哈希值等。
  • 您使用(單字符)字符串作爲鍵,並且字符串散列是隨機的。如果你做print(hash(message))甚至只是print(hash('c')),那麼你會看到,不同的運行和下一個運行。

因此,由於順序依賴於散列,並且哈希從一次運行變爲下一次,所以當然可以得到不同的命令。

在另一方面,如果你在同一個運行重複,你可能會得到同樣的順序:

message = 'Some random string of words' 
for _ in range(10): 
    dictionary= {} 
    for character in message: 
     dictionary.setdefault(character,0) 
     dictionary[character] = dictionary[character] + 1 
    print(dictionary) 

我只是跑了,它的印刷以相同的順序全10回,如預期。然後我再次運行它,並打印出不同的順序,但所有十次都是一樣的。如預期。

+0

啊 - 當然......這非常有意義!我錯過了場景的哈希元素...謝謝! – DCaugs

+1

@DCaugs在其他語言中更明顯,它明確地調用它們的'dictionary'等價於'HashMap'。 – RoadieRich

2

dict s本質上是無序的。

Python docs

鍵和值遍歷在非隨機的,不同的Python實現不同而不同,取決於插入和刪除的字典的歷史以任意順序。

編輯

你的代碼的替代品,正確地完成你的目標是使用OrderedCounter

from collections import Counter, OrderedDict 

class OrderedCounter(Counter, OrderedDict): 
    'Counter that remembers the order elements are first encountered' 

    def __repr__(self): 
     return '%s(%r)' % (self.__class__.__name__, OrderedDict(self)) 

    def __reduce__(self): 
     return self.__class__, (OrderedDict(self),) 

message = 'Some random string of words' 
print(OrderedCounter(message.upper())) 
+1

本說明不解釋爲什麼在同一實現上多次運行之間的順序更改以及相同的插入和刪除歷史記錄。 (另外,在解釋器運行之間是隨機的,但不是在一次運行中) – viraptor

+0

@viraptor我不記得作爲OP的問題。 OP只是問他爲什麼每次他/她運行程序時字典都以不同的順序打印,這就是我回答的問題。 – pzp

+0

「如果你多次運行這個操作,你會注意到每次計數都會以看似隨機的順序返回,這是爲什麼?」 - >引用的文檔沒有解釋這一點。在同一次運行中多次重複原始代碼,順序將是任意的,但始終相同。 – viraptor

1

dict實現是專爲看起坐要快的方式高效。即使隨着dict的大小增加。這意味着關鍵訂單可能會發生變化。

如果密鑰的順序對您很重要,請嘗試使用collections中的ordereddict

+0

這是有道理的,但爲什麼當字典的大小不變時,訂單會改變?這就是我試圖包裹我的頭 - 如果你反覆執行相同的簡單代碼,爲什麼Python決定以不同的順序返回結果? – DCaugs

2

出現這種情況是由於安全原因。當你編寫任何外部用戶可以提供以字典結尾的數據的應用程序時,你需要確保他們不知道散列結果會是什麼。如果他們這樣做,他們可以確保他們提供的每個新條目都會散列到同一個文件夾中。當他們這樣做時,最終會以「O(1)」的檢索結果取代O(n),因爲字典中的每個get()都會得到相同的bin並且必須遍歷其中的所有項目。 (或考慮其他處理請求的時間可能更長)

查看https://131002.net/siphash/siphashdos_appsec12_slides.pdf瞭解更多信息。

幾乎所有語言都通過在啓動時生成一個隨機數並將其用作散列種子來防止這種情況,而不是從某些預定義數字開始,如0

+0

非常好 - 謝謝。除了上面的Stefan的回答之外,這很有意義。我真的沒有考慮過這裏的安全角度。 – DCaugs