views:

106

answers:

5

If I have two identical sets, meaning a == b gives me True, will they have the same iteration order? I tried it, and it works:

>>> foo = set("abc")
>>> bar = set("abc")
>>> zip(foo, bar)
[('a', 'a'), ('c', 'c'), ('b', 'b')]

My question is, was I lucky, or is this behavior guaranteed?

+4  A: 

You were lucky, the order is not guaranteed. The only thing that's guaranteed is that the sets will have the same elements.

If you need some sort of predictability, you could sort them like this: zip(sorted(foo), sorted(bar)).

Cristian Ciupitu
A: 

I'd say you got lucky. Though, it might also be that, since the elements in the set were the same, they were stored in the same order. This behavior is not something you'd want to rely on.

Gangadhar
+3  A: 

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')]

P.S. I have no idea if the __hash__ override is necessary. I just tried something I thought would break this, and it did.

katrielalex
Well, it proves the point though. If there are hash collisions, the order would probably depend on something beyond my control. Thanks!
Space_C0wb0y
+12  A: 

It wasn't just a coincidence that they came out the same: the implementation happens to be deterministic, so creating the same set twice produces the same ordering. But Python does not guarantee that.

If you create the same set in two different ways:

n = set("abc")
print n

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

...you can get different ordering:

set(['a', 'c', 'b'])
set(['a', 'b', 'c'])
Jason Orendorff
+1 Simplest counterexample.
katrielalex
Another very good counter-example. Thanks!
Space_C0wb0y
You are perfectly right : it wasn't a coincidence. For example, it seems that if you create the same set directly without removing anything, you get always the same ordering : For example : set("abbacca") gives set('a','c','b') as well as set("bbabbca"). This behavior is non-stochastic and linked to the implementation. It would be interesting to look at the source code of python :)(But in all cases it would be definitely a bad idea to rely on it :) )
Elenaher
@Elenaher Actually you can't even rely on that. Try `set('ai')` and `set('ia')`. They produce different ordering for me. (I think this is because 'a' and 'i' have the same hash code modulo 8, plus another minor coincidence or two.)
Jason Orendorff
@Jason Orendorff : Indeed you are totally right (I tried also with "b" and "j" (same hash % 8 too) and I also got different ordering).
Elenaher
+1  A: 

Yes, you were lucky. See for example:

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)

Which gave me the result:

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)]
Steven Rumbalski