Python

Python namedtuple のススメ ~json ファイルの読み込み~

Python における namedtuple の良さを、jsonファイルのデータを読み込む例で紹介します。

この記事でやること

  • namedtuple の利点を説明し、Python における json の読み込みを例にそれを確かめます。
  • Python で json ファイルを namedtuple に変換する例を紹介します。

 

namedtuple の利点

namedtuple による利点は簡潔に言うと2つです。

  1. 記述が簡潔になる(特に階層が深い時に)
  2. イミュータブルである(値が書き換わらない)

これらが json ファイルを読み込む上で特に有効となります。

namedtuple のざっくりした概要

namedtuple は tuple に名前がついたものです。標準ライブラリの collections から import することで使えます。

# tuple による記述
song_tuple = ('symphony', 6, [1808, 12, 22])

# namedtuple による記述
from collections import namedtuple
Music = namedtuple('Music', ['genre', 'number', 'premire'])
song_ntuple = Music('symphony', 6, [1808, 12, 22])

 

song_tuple は各要素にインデックスでしかアクセスできないのに対し、song_ntuple はインデックスだけでなくフィールド名でのアクセスができます。

print(song_tuple[0])
# 'symphony'

print(song_ntuple[1])
# 3
print(song_ntuple.premire)
# [1808, 12, 22]

 

また、namedtuple は tuple の一種であるため、保持しているデータは代入によって値を変更することができない(イミュータブル)という特徴があります。

song_ntuple.number = 9
# AttributeError: can't set attribute

 

Python における json の読み込み

例えば以下のような musicdata.json を読み込むとします。

{
    "Beethoven": {
      "symphony": {
        "no3":{
          "title": "英雄",
          "key": "E-flat",
          "premiere": {
            "year": 1804,
            "month": 12
          }
        },
        "no5":{
          "title": "運命",
          "key": "c",
          "premiere": {
            "year": 1808,
            "month": 12,
            "date": 22
          }
        },
        ...
      },
      "piano_sonata": {
        ...
      }
    },
    ... 
  }

 

Python でこれを読み込むときは、標準ライブラリの json を用います。

import json

with open('musicdata.json', 'rb') as f:
  music_dict = json.load(f) 

print(music_dict)
# {'Beethoven': {'symphony': {'no3': {'title': '英雄', 'key': 'E-flat', 'premiere': {'year': 1804, ...

出力からわかるように、Python で json ファイルを読み込むと dict 型に変換されますが、これはファイルによっては不便に感じることがあります。

例えばここから交響曲第3番の初演の年を得ようとした時には、このように記述する必要があります。

au_year = music_dict['Beethoven']['symphony']['No3']['premiere']['year']

[‘ ‘] って何回書けばいいんだ……

namedtuple のメリット

……って思った人のための namedtuple です。

namedtuple はその名の通り名前がついた tuple ですが、「それぞれの値に対応する名前がある」と考えると、感覚的には辞書に近いものだと思えるでしょう。

読み込んだ json ファイルに対して namedtuple を用いた変換を行うことで以下のように表記できます。

au_year = music_dict.Beethoven.symphony.No3.premiere.year

[‘ ‘] を書かなくていい分だいぶ楽になります。またイミュータブルなので途中で誤って値が書き変わってしまうという心配もなくなります。

json ファイルから namedtuple に変換

前章の最後で説明した namedtuple の形式への変換方法の例を以下に示します。「jsonファイルから」と書いてますが、以下の dict_to_ntuple() は json を読み込んで得られた dict ファイルを与えることで namedtuple に変換したものを得られます。

from collections import namedtuple
from typing import Dict, Tuple, List, Union

def dict_to_ntuple(name: str, d: Union[Dict, List]) -> Tuple:
    if type(d) == list:
        items = enumerate(d)
    elif type(d) == dict:
        items = d.items()

    intermed_dict = {}
    for k, v in items:
        if type(v) == list:
            intermed_dict[k] = tuple(v)
        elif type(v) == dict:
            intermed_dict[k] = dict_to_ntuple(k, v)
        else:
            intermed_dict[k] = v
    return namedtuple(name, sorted(intermed_dict))(**intermed_dict)

json ファイルを読み込んだ dict 内の value では、数値や文字列などの値以外には list か dict の要素しかないことを利用して、再帰的に namedtuple に変換しています。

kai
アナリティクス&デベロップメント所属。 平日に大きなデータを扱い、週末は大きな楽器を扱う