The order of a dict is completely determined by the hashing function of the object (and insertion order if there are hash collisions). Integers hash to themselves (at least up to sys.maxint
):
>>> hash(1)
1
The (C)python implementation takes the hash value of the object and takes a few bits to determine the index in the table. How many bits it takes depends on the length of the dictionary. By default, the dict has 8 available slots, so the numbers 0
and 8
will collide. We can see this as follows:
>>> d1 = {}
>>> d1[0] = 'foo'
>>> d1[8] = 'bar'
>>> d1
{0: 'foo', 8: 'bar'}
>>>
>>> d2 = {}
>>> d2[8] = 'bar'
>>> d2[0] = 'foo'
>>> d2
{8: 'bar', 0: 'foo'}
Since 0
and 8
collided in our dictionary, insertion order appears to have been maintained. 0
takes the first available slot (after all, no matter how many bits you take from 0
, you'll get 0
). 8
tries to take that slot as well. If that slot is taken, however, collision resolution takes over and python inserts that value in some later slot.
Of course, if your dictionary happens to have more than ~5 elements, it will be resized (I think to 16, but don't quote me on that) and 0
and 8
will no longer collide...
>>> d1 = {x:x for x in range(1, 6)}
>>> d1[0] = 0
>>> d1[8] = 8
>>> d1
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 8: 8}
>>> d2 = {x:x for x in range(1, 6)}
>>> d2[8] = 8
>>> d2[0] = 0
>>> d2
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 8: 8}
Note, the (sorted) order is preserved (not insertion order) which means that every integer got it's preferred spot in the hash table (no collisions). I think that the dict gets resized when it is about 2/3rds full.
Note, this is purely academic -- the python specification doesn't say this is how it works and so it could change at any time. Please don't rely on this behavior. Most of this can be gleaned from comments in the source code and documentation that sits next to it...