2016-08-05 80 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