⚓️

Pythonで構造体みたいなデータを扱いたいときに標準で使える候補

2024/11/28に公開

はじめに

Pythonで型を意識しつつ,構造体のようなデータを扱いたいときに,標準で使える候補として以下の3つがあります.

  • dataclasses
  • NamedTuple
  • TypedDict

それぞれ使い方と簡単な特徴を紹介します.

dataclasses

dataclassesは以下のように使います.

from dataclasses import dataclass

@dataclass
class MyStruct:
    id: int
    name: str
    active: bool

# 使用例
data = MyStruct(id=1, name="Test", active=True)
print(data) # MyStruct(id=1, name='Test', active=True)

# idの値を変更する
data.id = 2
print(data) # MyStruct(id=2, name='Test', active=True)

# idの値をstr型で変更する
data.id = "a"
print(data) # MyStruct(id='a', name='Test', active=True)
# エラーになりません

# 新しい属性を追加してみる
data.description = "説明"
print(data) # MyStruct(id='a', name='Test', active=True)
print(data.description) # 説明
# 追加されます

# 比較が可能です
data2 = MyStruct(id="a", name="Test", active=True)
print(data == data2) # True

@dataclass
class OtherStruct:
    id: int
    name: str
    active: bool

# 構造が同じでも,クラスが異なれば比較はFalseになります
data3 = OtherStruct(id="a", name="Test", active=True)
print(data == data3) # False

# イミュータブルにすることもできます
@dataclass(frozen=True)
class MyImmutableStruct:
    id: int
    name: str
    active: bool

# インスタンス作成
data = MyImmutableStruct(id=1, name="Test", active=True)
print(data)  # MyImmutableStruct(id=1, name='Test', active=True)

# 属性を変更しようとするとエラーになります
# data.id = 2  # dataclasses.FrozenInstanceError: cannot assign to field 'id'

簡単な特徴

  • イミュータブルにもできる.
  • パラメータでいろいろ指定できる.

NamedTuple

NamedTupleは以下のように使います.

from typing import NamedTuple

class MyStruct(NamedTuple):
    id: int
    name: str
    active: bool

data = MyStruct(id=1, name="Test", active=True)
print(data) # MyStruct(id=1, name='Test', active=True)

# 値を変更しようとするとエラーになります
# data.id = 2 # AttributeError: can't set attribute

data2 = MyStruct(id=1, active=True, name="Test") # #順番を変えてもOK
print(data2) # MyStruct(id=1, name='Test', active=False)

# 普通のタプルと比較も可能です.
data3 = (1, "Test", True)
print(data == data2) # True
print(data == data3) # True

# それぞれに取り出すことができます
id, name, active = data
print(name) # Test

# 順番が重要です
id, active, name = data
print(name) # True 

気を付けるべき使用例としては,以下のようなものがあります.


class News(NamedTuple):
    north: str
    east: str
    west: str
    south: str

class EastNorth(NamedTuple):
    east: str
    north: str

en = EastNorth(east="東", north="北")
print(en) # EastNorth(east='東', north='北') 
news = News(*en, west="西", south="南")
print(news) # News(north='東', east='北', west='西', south='南')
# northとeastが逆になってしまう

Namedですが,それを用いてうまく代入されるわけではないです.

簡単な特徴

  • NamedTupleは後から値を変更できない.
  • タプルとして等しければ,それらは等しいと判定される.

TypedDict

TypedDictは以下のように使います.

from typing import TypedDict

class MyStruct(TypedDict):
    id: int
    name: str
    active: bool

# 使用例
data: MyStruct = {"id": 1, "name": "Test", "active": True}
print(data) # {'id': 1, 'name': 'Test', 'active': True}

# .(ドット)でアクセスはできない
# data.id = 2 #'dict' object has no attribute 'id'

# 辞書のように更新できる
data["id"] = 2
print(data) # {'id': 2, 'name': 'Test', 'active': True}

# インデックスではなく,keyとなる.
data[0] = 3
print(data) # {'id': 2, 'name': 'Test', 'active': True, 0: 3}

# 辞書と比較が可能
data2 = {'id': 2, 'name': 'Test', 'active': True, 0: 3}
print(data == data2) # True

# タイプはdict
print(type(data)) # <class 'dict'>
print(type({"a": 1})) # <class 'dict'>

簡単な特徴

  • TypedDictは辞書として扱われる.

まとめ

どれでも良いなら,dataclassesを使うかな,というところです.

GitHubで編集を提案

Discussion