LoginSignup
6
4

More than 1 year has passed since last update.

Pythonのdataclassにおけるmetadata

Posted at

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

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4