Edited at

Python 3.7でdictが順序を保存するようになって良かったとしみじみ思う

Python 3.7から(非公式には3.6から) dict がキーの順序を保証するようになりました(collections.OrderedDict相当の動作になった)。

今更ながら「dict が順序を保存するようになって良かったナァ」とシミジミ感じているので、その気持ちをダンプします。


良かったこと

例えば pandas でデータ集計をした時に DataFrameのカラム名を日本語に直した上で順序も入れ替えたい、なんてことはあるでしょう。

Python 3.7以降では、こんな風に辞書を1つ書けばOK。

import pandas as pd

df = pd.DataFrame(...) # 何処かから DataFrame を取得済みとする。カラムは "age", "name", "salary"

# カラムの順序を "name, age, salary" にした上で、日本語に置き換えたい
mapper = {
"name": '名前',
"age": "年齢",
"salary": "年収",
}

output = df[mapper.keys()].rename(columns=mapper)
# 名前 年齢 年収
# 0 Alice 21 100
# 1 Bob 32 1200
# 2 Charlie 43 2300

Python 3.5 では、辞書の順序が保存されないので、カラムの順序が入れ替わってしまうかもしれません。

#      名前    年収  年齢

# 0 Alice 100 21
# 1 Bob 1200 32
# 2 Charlie 2300 43

カラムの順序を保存するには、別途順序を表すリストを用意するか・・・

mapper = {

"name": '名前',
"age": "年齢",
"salary": "年収",
}

keys = ["name", "age", "salary"]

output = df[keys].rename(columns=mapper)

順序を保証する OrderedDict を使うしかありませんでした。

from collections import OrderedDict

mapper = OrderedDict([
("name", '名前'),
("age", "年齢"),
("salary", "年収"),
])

output = df[mapper.keys()].rename(columns=mapper)

でも、リストを別に用意するのDRYじゃありません。

OrderedDictはDRYではあるんですが、見た目が「マッピング」っぽくありません。カッコが多くて書くのも面倒だし、from 〜 import 〜 も必要です。

Python 3.7 なら、DRYで見やすい辞書リテラルを、何のデメリットも無く使えるのです。やったね!


良かったこと・その2

今までも OrderedDict という「順序が保持された辞書」があったのですが、

所詮は普通にclass文で定義された普通のクラスにすぎません。組み込み型ではないのです。

なので、printfデバッグの際など、不細工なものを見ることになります。

from pprint import pprint

from collections import OrderedDict

# この辞書はYAMLファイルか何かから読み込んだと思いねえ
d = OrderedDict({"name": "Alice", "age": 21, "salary": 100, "foo": 1000})

# printfデバッグしてみよう
pprint(d, width=30)

# OrderedDict([('name',
# 'Alice'),
# ('age', 21),
# ('salary', 100),
# ('foo', 1000)])

# うげげ!?

もちろん dict なら全く問題ありません。

d = {"name": "Alice", "age": 21, "salary": 100, "foo": 1000}

# printfデバッグしてみよう
pprint(d, width=30)

# {'age': 21,
# 'foo': 1000,
# 'name': 'Alice',
# 'salary': 100}

# 見やすい!


良かったこと・その3

YAMLやJSONなどを扱うライブラリを開発している方は、こんな議論を見たことがないでしょうか?


「YAMLを読むとき、キーがファイルに書かれた順序通りになるようにできませんか?」

「Pythonのdictは順序を保存しないので無理です」

「ならOrderedDictを使えばいいじゃないですか」

OrderedDictを返して既存のクソコードが動かなくなったらどうするんですか!?」

「YAMLも順序を保存しないのが仕様である!OrderedDictは人類の堕落につながる!!」

「じゃあ、間をとってOrderedDictを返す ordered_dict=True というオプションを追加しましょうか」

「毎回ordered_dict=Trueと書くのダルいので、デフォルト設定を上書きできませんか?」

「いいこと思いついた! dict_class という引数で辞書のクラスを変えられるようにしよう!」

「それOrderedDict以外に使う機会あるんですか?」


まあ、自転車置き場の議論です。メリットは少ない割に、誰かが現状を調査して、何かしらの判断を下さなければなりません。

でも、組み込みの dict が最初から順序を保存していれば、こんな議論はそもそも不要なのです。やったね!


むすび

みなさんも、ぜひ dict の順序保証をご活用ください。

OrderedDict よ、今までありがとう。そしてさようなら!!