Pythonのdataclassにおけるmetadata

TL;DR
dataclassのmetadataを使うと、メンバ変数にメタ情報を付与できる。
メタ情報を付与することで、実装を簡略化できるケースがある。
※metadataはサードパーティー製用なので本来の意図とは違う可能性あり

dataclasses(dataclass)とは
Python3.7から追加されたデータを格納するクラスを簡単に定義できる機能を提供するモジュールです。
こんな感じ
from dataclasses import dataclass

@dataclass
class Company:
“””
会社クラス
“””
name: str
address: str
average_age: int
description: str

if __name__ == “__main__”:
company = Company(name=”創屋”, address=”石川県白山市”, average_age=30, description=”創屋はAIの会社です”)

metadataとは
以下公式より参照
metadata: これはマッピングあるいは None に設定できます。 None は空の辞書として扱われます。
この値は MappingProxyType() でラップされ、読み出し専用になり、 Field オブジェクトに公開されます。
これはデータクラスから使われることはなく、サードパーティーの拡張機構として提供されます。
複数のサードパーティーが各々のキーを持て、メタデータの名前空間として使えます。

C#でいうところのAttributeと似たようなものですね。
dataclassでフィールドを定義する際にfield関数を使用してmetadataを設定することができます。
こんな感じ
from dataclasses import dataclass, field

@dataclass
class Company:
name: str = field(default=””, metadata={“metadata”: “metadata_value”})
address: str = field(default=””, metadata={“metadata”: “metadata_value”})
average_age: int = field(default=0, metadata={“metadata”: “metadata_value”})
description: str = field(default=””)

if __name__ == “__main__”:
company = Company(name=”創屋”, address=”石川県白山市”, average_age=30, description=”創屋はAIの会社です”)

metadataはfields関数で取得したfiledオブジェクトでしか参照できません。
from dataclasses import dataclass, field, fields

@dataclass
class Company:
name: str = field(default=””, metadata={“metadata”: “metadata_value”})
address: str = field(default=””, metadata={“metadata”: “metadata_value”})
average_age: int = field(default=0, metadata={“metadata”: “metadata_value”})
description: str = field(default=””)

def show_metadata(self):
“””
Filed表示
“””
for field_ in fields(self):
print(field_.name, field_.metadata)

if __name__ == “__main__”:
company = Company(name=”創屋”, address=”石川県白山市”, average_age=30, description=”創屋はAIの会社です”)
company.show_metadata()

実行結果
name {‘metadata’: ‘metadata_value’}
address {‘metadata’: ‘metadata_value’}
average_age {‘metadata’: ‘metadata_value’}
description {}

metadataの活用方法

バリデーション
from dataclasses import dataclass, field, fields

@dataclass
class Company:
name: str = field(default=””, metadata={“validation”: [lambda x: len(x) == 0]})
address: str = field(default=””, metadata={“validation”: [lambda x: len(x) == 0]})
average_age: int = field(default=0, metadata={“validation”: [lambda x: not x > 0]})
description: str = field(default=””)

def validation(self):
“””
validation
“””
for field_ in fields(self):
validations = field_.metadata.get(“validation”, [])
for validation in validations:
assert not validation(getattr(self, field_.name))

フォーマット設定
from dataclasses import dataclass, field, fields
from datetime import datetime

@dataclass
class Company:
name: str = field(default=””)
foundation_date: datetime = field(default=datetime.now(), metadata={“format”: “%Y%mM%d”})

DBのフィールド名設定
from dataclasses import dataclass, field, fields

@dataclass
class Company:
name: str = field(default=””, metadata={“field_name”: “company_name”})
address: str = field(default=””, metadata={“field_name”: “company_address”})
average_age: int = field(default=0, metadata={“field_name”: “avg_age”})
description: str = field(default=””, metadata={“field_name”: “memo”})

ORMがあれば不要なのですが、ORM使わないケース等では有用かと。

その他
その他には処理対象をフィルタするフラグを付けたり、CSV(エクセル)の書き込み位置を付与したり…
色々と出番はあるかと思います。

最後に
dataclassのmetadataの紹介でした。
Modelの定義はdataclassでできることが多いので、dataclass自体の利用頻度も上がっているのではと思います。
metadataも使いこなせればメンテナンス性の高いスマートなコードになりそうですね。
是非使ってみてください。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする