LoginSignup
15
9

More than 5 years have passed since last update.

pythonのdictのキーの順番の話

Last updated at Posted at 2019-01-31

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() の値に依るので、

a.py
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を指定しても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.

おわり。

15
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
9