いろいろ備忘録日記

主に .NET と Python絡みのメモを公開しています。

Pythonメモ-07 (collections.namedtuple, 名前付きのフィールドを持つタプル)

namedtupleについてのメモです。

概要

むかーしむかしにpython触っていた時(多分 2.0 とかそのへん)には、無かったのですが

最近またpython触るようになって、まず気に入ったのがこの namedtuple さん。

軽いし、フィールドアクセスできるし、なんといっても immutable なのがイイ。ついでにreprも用意してくれるのでデバッグ時に見やすい。

collectionsとitertoolsパッケージに用意されているものは、全部素晴らしいですね。(あと、functools.partialも) 数はそんなに多くないけど、ほんと便利なライブラリ。

なんですが、結構周りで知らない人もいたりしたので、使い方のメモ書きです。

サンプル

        # ---------------------------------------------------------------
        # namedtupleは、「名前つきタプル」を作成する
        # 通常のタプルに、型名と名前つきのフィールドを付与出来るイメージ。
        # 通常のタプルと同様に、イミュータブルであることは同じ。
        # 値は、コンストラクト時に指定する。
        #
        # フィールド名は、カンマ区切りの文字列で渡すか
        # シーケンスを指定する
        # ---------------------------------------------------------------
        Person = collections.namedtuple('Person', 'name,age')
        p = Person(name='test', age=30)
        pr('Person', p)

        try:
            p.name = 'test2'
        except AttributeError as e:
            pr('namedtupleはイミュータブル', e)

        # ---------------------------------------------------------------
        # フィールド名にはシーケンスを指定することも出来る
        # ---------------------------------------------------------------
        field_names = ('name', 'age')
        Person2 = collections.namedtuple('Person2', field_names=field_names)
        p2 = Person2(name='test2', age=30)
        pr('Person2', p2)

        # ---------------------------------------------------------------
        # 存在しないフィールド名を指定するとエラー
        # ---------------------------------------------------------------
        try:
            p3 = Person2(name='test3', age=33, hoge=10)
        except TypeError as e:
            pr('存在しないフィールド名を指定', e)

        # ---------------------------------------------------------------
        # verbose引数にTrueを指定するとクラス宣言を標準出力に出してくれる
        # (*) Python ドキュメントをみると、この指定は deprecated 扱い。
        #     現在は、_source フィールドから取得するべきとのこと。
        # ---------------------------------------------------------------
        collections.namedtuple('Person3', 'name', verbose=True)

        # ------------------------------------------------------------
        # namedtuple は、通常のtupleと同様に利用できる。
        # さらに、以下の3つのメソッドを持つ。
        #   ・_make
        #   ・_asdict
        #   ・_replace
        # また、以下のプロパティも持つ
        #   ・_fields
        # ------------------------------------------------------------
        MyVal01 = cols.namedtuple('MyVal01', ['name', 'value'])
        obj1 = MyVal01('hello world', 'value01')

        pr('obj', obj1)

        # namedtuple は、__dict__ を持たない
        try:
            pr('__dict__', obj1.__dict__)
        except AttributeError as e:
            pr('__dict__', e)

        # namedtuple は、__slots__ に 空タプルが設定される
        pr('__slots__', obj1.__slots__)

        # ------------------------------------------------------------
        # _make メソッド
        # --------------------
        # 既存のsequence, iterable から新しいオブジェクトを構築する。
        # csvやデータベースなどの行からオブジェクトを作成するのに便利。
        # ------------------------------------------------------------
        rows = (['hello', 'value01'], ['world', 'value02'])
        for item in (MyVal01._make(row) for row in rows):
            pr('item', item)

        # ------------------------------------------------------------
        # _asdict メソッド
        # --------------------
        # フィールド名と値のOrderedDictを返す。
        # 戻り値が OrderedDict なので、フィールドの並び順の通りに取得できる。
        # (*) OrderedDictになったのは、python 3.1 から。
        # ------------------------------------------------------------
        obj_dict = obj1._asdict()
        pr('obj_dict', obj_dict)

        # 辞書から namedtuple を構築する場合は **kwargs 形式で渡す
        obj2 = MyVal01(**obj_dict)
        pr('obj2', obj2)
        pr('eq', obj1 == obj2)

        # ------------------------------------------------------------
        # _replace メソッド
        # --------------------
        # 指定したフィールドの値を置き換えた、新しい namedtuple を返す。
        # namedtuple は、immutableなので、常に新しいオブジェクトを返す。
        # ------------------------------------------------------------
        obj3 = obj2._replace(name='world hello', value='value03')
        pr('obj3', obj3)
        pr('eq', obj3 == obj2)

        # ------------------------------------------------------------
        # _fields プロパティ
        # --------------------
        # 定義されているフィールド名をタプルで返す。
        # ------------------------------------------------------------
        pr('_fields', obj3._fields)

        # ------------------------------------------------------------
        # namedtuple に、独自のメソッドを持たせる場合は
        # namedtuple を親クラスにしたクラスを新たに定義する。
        # ------------------------------------------------------------
        class MyVal02(cols.namedtuple('MyVal02', ['name'])):
            __slots__ = ()

            @property
            def upper_name(self):
                return self.name.upper()

        obj4 = MyVal02('hello world 2')
        pr('obj4.name', obj4.name)
        pr('obj4.upper_name', obj4.upper_name)

ソースは以下でも見れます。


以下、参考にした情報です。


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。