Python中集合的迭代顺序

18
如果我有两个完全相同的集合,也就是说 a == b 返回 True,那么它们的迭代顺序会相同吗?我已经尝试过,它可以工作:
>>> foo = set("abc")
>>> bar = set("abc")
>>> zip(foo, bar)
[('a', 'a'), ('c', 'c'), ('b', 'b')]

我的问题是,这是幸运还是行为有保证?


如果 a is b,我认为它们将具有相同的迭代顺序。不过,这并不是一个非常微妙的观点 =p - Katriel
5个回答

22

它们同时出现并非仅仅是巧合:实现恰好是确定性的,因此两次创建相同的集合会产生相同的顺序。但是Python并不保证这一点。

如果你用两种不同的方式创建相同的集合:

n = set("abc")
print n

m = set("kabc")
m.remove("k")
print m

您可以获得不同的排序:

set(['a', 'c', 'b'])
set(['a', 'b', 'c'])

你是完全正确的:这不是巧合。例如,如果您直接创建相同的集合而不删除任何内容,则始终会获得相同的排序方式:例如:set(“abbacca”)给出set('a','c','b'),以及set(“bbabbca”)。这种行为是非随机的,并与实现相关。有趣的是看看Python的源代码 :) (但在所有情况下,依赖它肯定是一个坏主意 :) ) - ThR37
3
@Elenaher 实际上,你不能完全依赖这个。尝试使用set('ai')set('ia'),它们会产生不同的顺序(我认为这是因为 'a' 和 'i' 在模8意义下具有相同的哈希码,再加上其他一些小巧合)。 - Jason Orendorff
确实,你是完全正确的(我也尝试了使用“b”和“j”(哈希值%8相同),并且得到了不同的排序)。 - ThR37

4

No.:

>>> class MyStr( str ):
...     def __hash__( self ):
...             return 0
...
>>> a = MyStr( "a" )
>>> b = MyStr( "b" )
>>> c = MyStr( "c" )
>>> foo = { a, b, c }
>>> foo
{'c', 'b', 'a'}
>>> bar = { b, a, c }
>>> foo is bar
False
>>> foo ==  bar
True
>>> list( zip( foo, bar ) )
[('c', 'c'), ('b', 'a'), ('a', 'b')]

顺便说一句,我不知道是否需要重写__hash__。我只是尝试了一些可能会破坏它的东西。


好的,这证明了这一点。如果存在哈希冲突,顺序可能取决于我无法控制的某些因素。谢谢! - Björn Pollex

4

你很幸运,订单不是保证的。唯一保证的是这些集合将具有相同的元素。

如果你需要某种可预测性,你可以像这样排序它们:zip(sorted(foo), sorted(bar))


1

是的,你很幸运。例如:

import random
r = [random.randint(1,10000) for i in range(20)]
foo = set(r)
r.sort(key=lambda _: random.randint(1,10000))
bar = set(r)
print foo==bar
print zip(foo, bar)

这给了我结果:

True
[(3234, 3234), (9393, 9393), (9361, 1097), (1097, 5994), (5994, 2044), (1614, 1614), (6074, 4377), (4377, 9361), (5202, 5202), (2355, 2355), (1012, 1012), (7349, 7349), (6198, 6198), (8489, 8489), (7929, 7929), (6556, 6074), (6971, 6971), (2044, 6556), (7133, 7133), (383, 383)]

0
我会说你很幸运。不过,也有可能是因为集合中的元素相同,它们被以相同的顺序存储。这种行为并不是你想要依赖的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接