Overview
python 3.6 未満だとdictがキーの序列を保持しないので、 items()
で取り出すと順番が不定になる。
というのを知らない、あるいは意識してない人が結構多いみたいなので、
現象と再現の方法を書いておきます。
ちなみに 3.7 からは言語仕様で順序が保証される ようになってます。
# 実装レベルではたぶん 3.6 でもなってると思う(出典忘れた)
環境
$ python -V
Python 2.7.15
2.7 で試してますが、 3.6 未満だと同じ結果になると思います。
# キーの順番自体は変わるはず
サンプルデータ
適当なdictが見当たらないので、 swaggerのサンプル使います。
$ wget https://petstore.swagger.io/v2/swagger.json
$ jq '.|keys' swagger.json
[
"basePath",
"definitions",
"externalDocs",
"host",
"info",
"paths",
"schemes",
"securityDefinitions",
"swagger",
"tags"
]
hashのseed値
hash()
のシード値は環境変数の PYTHONHASHSEED
の値を参照しており、この値を変えると、
$ PYTHONHASHSEED=123 python -c "print(hash('hoge'))"
-7331607872126641601
$ PYTHONHASHSEED=12345 python -c "print(hash('hoge'))"
5740883211462535735
同じ値でもhash値が変わります。
dictの items() メソッド
dictのキーも hash()
の値に依るので、
import json
with open("swagger.json") as fp:
a = json.load(fp)
for k, v in a.items():
print(k)
こんな感じで試すと、
ex1
$ PYTHONHASHSEED=123 python a.py
basePath
swagger
externalDocs
info
host
schemes
definitions
securityDefinitions
paths
tags
ex2
$ PYTHONHASHSEED=12345 python a.py
host
definitions
swagger
externalDocs
info
paths
schemes
tags
basePath
securityDefinitions
という感じになります。
再現させたい時は PYTHONHASHSEED
の値を固定すると良いでしょう。
回避方法
3.6未満のdictのキーに序列の保持を期待する場合は OrderedDict を使いましょう。
余談1: python 3.6 の場合
キーの順序が保持されてます。
$ python -V
Python 3.6.8
$ PYTHONHASHSEED=123 python a.py
swagger
info
host
basePath
tags
schemes
paths
securityDefinitions
definitions
externalDocs
$ PYTHONHASHSEED=12345 python a.py
swagger
info
host
basePath
tags
schemes
paths
securityDefinitions
definitions
externalDocs
余談2: random の seed
ちなみに random
の seed はまた別なので、
これを固定したい場合は別途 random.seed()
で指定する必要があります。
$ PYTHONHASHSEED=123 python -c "import random; random.seed(42); print(random.random())"
0.639426798458
$ PYTHONHASHSEED=12345 python -c "import random; random.seed(42); print(random.random())"
0.639426798458
cf.
おわり。