🐍

python + yaml 辞書順序問題

2021/07/24に公開

python + yaml で辞書(Dict)の順序を維持するということで、改めて調べてみた。

pythonのDictは順序を維持する 3.7以降

pythonのDictは挿入順を維持しないことが有名だったと思うが、もう古いらしい。3.7以降は維持する。
じゃあOrderdDictは不要かというと、そんなこともないらしい。公式にもあるが、

  • Dictどうしで比較する
  • 順序もちゃんと見る必要がある

は以前Orderdが必要のようだ。まあ、めったにないと思うけど

3.7以降でも PyYAMLだと順序狂うぞ?

import yaml as c_yaml
d = {"hoge": "fuga", "a": 10, "c": 120, "b": 0 }
c_yaml.safe_(d)
print(c_yaml.safe_dump(d))

a: 10
b: 0
c: 120
hoge: fuga

並び替えしてるぞ。ちょっと調べたら、sale_dumpに引数送ればいいらしい

print(c_yaml.safe_dump(d, sort_keys=False))

hoge: fuga
a: 10
c: 120
b: 0

ほんまや

他のライブラリruamel.yaml はどうや?

ruamel.yamlを試してみた。Loadから

from ruamel.yaml import YAML

inp = """\
# example
name:
  # details
  family: Smith   # very common
  given: Alice    # one of the siblings
"""
yaml = YAML()
yaml.load(inp)

Out[X]: ordereddict([('name', ordereddict([('family', 'Smith'), ('given', 'Alice')]))])

いきなりOrderdDictでくるのか。OrderdのDumpは想像どおり、順序維持したので、敢えてDictをDumpしてみる

d = {"hoge": "fuga", "a": 10, "c": 120, "b": 0 }

import sys
yaml.dump(d, sys.stdout)

hoge: fuga
a: 10
c: 120
b: 0

Dictでもとくに何もせずとも維持された。

jsonはどうや?

d = {"hoge": "fuga", "a": 10, "c": 120, "b": 0 }

import json
json.dumps(d)

# Out[x]: '{"hoge": "fuga", "a": 10, "c": 120, "b": 0}'

json.dumps(d, sort_keys=True)
# Out[x]: '{"a": 10, "b": 0, "c": 120, "hoge": "fuga"}'

デフォルトで sort_keys=False のようです。 yamlと仕様が逆。

学び

  • 辞書の順序が狂うのはPythonの昔からの仕様ではなくて、Python単体だと辞書順序は維持される、3.7からは
  • 辞書の順序が狂うではなく、PyYamlにアルファベット順に並べ替えられていた
    • そもそもPython単体で辞書の順番変わるのはアルファベット順ではなく、ランダム
  • jsonはデフォルトでアルファベットで並び替えをしない、PyYAMLと逆になっていたのが不幸

Discussion