51
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-09-19

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 よ、今までありがとう。そしてさようなら!!

51
29
1

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
51
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?