I recently switched from Python 2.7 to Python 3.3, and it seems that while in Python 2 the ordering of dictionary keys was arbitrary but consistent, in Python 3 the ordering of the keys of a dictionary obtained with e.g. vars()
appears non-deterministic.
If I run:
class Test(object): pass
parameters = vars(Test)
print(list(parameters.keys()))
in both Python 2.7 and Python 3.3, then:
Python 2.7 consistently gives me
['__dict__', '__module__', '__weakref__', '__doc__']
With Python 3.3, I can get any random order – for example:
['__weakref__', '__module__', '__qualname__', '__doc__', '__dict__'] ['__doc__', '__dict__', '__qualname__', '__module__', '__weakref__'] ['__dict__', '__module__', '__qualname__', '__weakref__', '__doc__'] ['__weakref__', '__doc__', '__qualname__', '__dict__', '__module__']
Where does this non-determinism come from? And why is something like
list({str(i): i for i in range(10)}.keys())
… consistent between runs, always giving
['3', '2', '1', '0', '7', '6', '5', '4', '9', '8']
… ?
Update: In Python 3.6,
dict
has a new implementation which preserves insertion order. From Python 3.7, this order-preserving behaviour is guaranteed:This is the result of a security fix from 2012, which was enabled by default in Python 3.3 (scroll down to "Security improvements").
From the announcement:
As noted above, the last, capitalized bit is no longer true in Python 3.3.
See also:
object.__hash__()
documentation ("Note" sidebar).If absolutely necessary, you can disable hash randomization in versions of Python affected by this behaviour by setting the
PYTHONHASHSEED
environment variable to0
.Your counterexample:
… does not in fact always give the same result in Python 3.3, although the number of different orderings is limited due to the way hash collisions are handled:
As noted at the beginning of this answer, that's no longer the case in Python 3.6: