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も使いこなせればメンテナンス性の高いスマートなコードになりそうですね。
是非使ってみてください。