いろいろ備忘録日記

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

Pythonメモ-13 (collection.namedtupleとtyping.NamedTuple)

概要

前にcollections.namedtupleについての記事を書いていたのですが

devlights.hatenablog.com

以下のページをみてたら、typing.NamedTupleというものもあるとのこと。

dbader.org

pythonのドキュメントを見ると、collections.namedtupleの型付き版とのこと。

C#とかに慣れている人だと、こっちの方が親近感が湧きますね。

使い方は、collections.namedtupleで

MyData = collections.namedtuple('MyData', ['id', 'name', 'age'])

とする場合、typing.NamedTupleだと

class MyData(typing.NamedTuple):
    id: int
    name: str
    age: int

となります。構築される中身はどちらも全く同じになります。

明示的に型ヒントを付けられるのがいい感じ。また当然ですが

ここで付与している型ヒントは、なんら強制力を持ちません。

あくまで型ヒント。なので、どんな値でも入ります。

collections.namedtupleがイミュータブルなのと同じで

typing.NamedTupleもイミュータブルです。

サンプル

import collections
import dis
import sys
import typing

class MyData(typing.NamedTuple):
    id: int
    name: str
    age: int


MyData2 = collections.namedtuple('MyData2', 'id name age')

m1 = MyData(1, 'name01', 33)
m2 = MyData2(1, 'name22', 22)
pr('typing.NamedTuple', m1)
pr('collections.namedtuple', m2)

pr('getsizeof(typing.NamedTuple)', sys.getsizeof(m1))
pr('getsizeof(collections.namedtuple)', sys.getsizeof(m2))

try:
    m1.name = 'name02'
except AttributeError as e:
    pr('typing.NamedTuple is immutable', e)

try:
    m2.name = 'name02'
except AttributeError as e:
    pr('collections.namedtuple is immutable', e)

hr('dis.dis(MyData)')
dis.dis(MyData.__new__)

hr('dis.dis(MyData2)')
dis.dis(MyData2.__new__)

実行すると以下のようになります。

typing.NamedTuple=MyData(id=1, name='name01', age=33)
collections.namedtuple=MyData2(id=1, name='name22', age=22)
getsizeof(typing.NamedTuple)=72
getsizeof(collections.namedtuple)=72
typing.NamedTuple is immutable=AttributeError("can't set attribute",)
collections.namedtuple is immutable=AttributeError("can't set attribute",)
----------------dis.dis(MyData)----------------
 14           0 LOAD_GLOBAL              0 (_tuple)
              2 LOAD_ATTR                1 (__new__)
              4 LOAD_FAST                0 (_cls)
              6 LOAD_FAST                1 (id)
              8 LOAD_FAST                2 (name)
             10 LOAD_FAST                3 (age)
             12 BUILD_TUPLE              3
             14 CALL_FUNCTION            2
             16 RETURN_VALUE
----------------dis.dis(MyData2)----------------
 14           0 LOAD_GLOBAL              0 (_tuple)
              2 LOAD_ATTR                1 (__new__)
              4 LOAD_FAST                0 (_cls)
              6 LOAD_FAST                1 (id)
              8 LOAD_FAST                2 (name)
             10 LOAD_FAST                3 (age)
             12 BUILD_TUPLE              3
             14 CALL_FUNCTION            2
             16 RETURN_VALUE

バイトコードを表示しても、全く同じですね。

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

try-python/typing01.py at master · devlights/try-python · GitHub

参考情報

Records, Structs, and Data Transfer Objects in Python – dbader.org

26.1. typing — 型ヒントのサポート — Python 3.6.1 ドキュメント


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

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