dictknifeにdictmapを追加してみた

github.com

はじめに

dictknifeにdictmap()を追加してみた。

round dict

例えばstack overflowのこの質問に対する回答がシンプルになる。

これを

d = [
    {
        'A': 0.700000000,
        'B': 0.255555555
    }, {
        'B': 0.55555555,
        'C': 0.55555555
    }, {
        'A': 0.255555555,
        'B': 0.210000000,
        'C': 0.2400000000
    }
]

こうしたいとのこと。

expected = [
    {
        'A': 0.70,
        'B': 0.25
    },
    {
        'B': 0.55,
        'C': 0.55
    },
    {
        'A': 0.25,
        'B': 0.21,
        'C': 0.24
    },
]

こういう感じで書けるようになった。

from dictknife import dictmap  # noqa
dictmap(lambda x: round(x, 2) if isinstance(x, float) else x, d)

# [{'A': 0.7, 'B': 0.26}, {'C': 0.56, 'B': 0.56}, {'C': 0.24, 'A': 0.26, 'B': 0.21}]

(上の入力に限ってなら以下でも良い)

dictmap(lambda x: round(x, 2), d)

ところでmutableとimmutable

ところで元の質問の回答はmutableで元の辞書を破壊的に更新するものだけれど。新しい同様の形状のdictを作り直すみたいな操作も考えられる。 実際に上のdictmap()で行われているのは新しいdictを作り直す操作。

破壊的に更新したい場合には以下の様にすれば良い。

import copy


d0 = copy.copy(d)
d1 = dictmap(lambda x: round(x, 2) if isinstance(x, float) else x, d, mutable=True)

assert id(d0) == id(d1)  # idは同じ(元のobject)

補足説明

ちょっとだけ補足しておくと元の値と同じdictのオブジェクトを使う。例えば渡された値がOrderedDictならOrderedDictが使われる。

元々欲しかった機能

dictmap()は実は副次的に追加したもので元々欲しかったのはdict内の数値っぽい値の文字列を数値に直す機能。 特にcsvやtsvに変換されたものを読み込む時に文字列として解釈されるのがわりとだるかった。 (ちなみに直接上のdictmap()が使われているわけではない。immutableな操作とmutableな操作を使い分ける部分が使われている)

テキトウに名前はguess()にしているけれど変えるかもしれない。こういう感じに動く。

この値の文字列部分がfloatやintに変換される。

v = [
    {
        "ints": [{
            "zero": "0",
            "nums": ["10", "-2000"]
        }],
        "floatnums": [{
            "full": "0.1",
            "mini": ".1",
            "epsilon": "5.551115123125783e-17"
        }],
        "infs": ["inf", "-inf"]
    }
]
guess(v)

こういう感じ。

[
    {
        'floatnums': [{
            'full': 0.1,
            'mini': 0.1,
            'epsilon': 5.551115123125783e-17
        }],
        'ints': [{
            'nums': [10, -2000],
            'zero': 0
        }],
        'infs': [float('inf'), float('-inf')]
    }
]

infとかnanの扱いはとりあえずpythonのそれだけ。