[Python]設計に活かすデータクラス(dataclasses)の使い方

2021年7月22日木曜日

Python

Python3.7以降から導入されたdataclasses
本記事では、dataclassesの導入ポイントや使い方を紹介します

アイキャッチ

dataclassesとは?

dataclassesとは、python 3.7以降に搭載されているクラスモジュールです。
データを格納することに特化したクラスとなっており__init__関数の自動生成等を
行い、データクラスの実装負担を軽減してくれます。

dataclassesと定義する意義

Model-View-Controllerに分離するMVCアーキテクチャや、プレゼンテーション層、アプリケーション層、データ層に分離する3層アーキテクチャなど比較的初期のアーキテクチャからデータ部分は分離の対象とされてきました

MVCアーキテクチャの概念図
MVCアーキテクチャ

3層アーキテクチャの概要図
3層アーキテクチャ


それだけデータを持つモジュールと、データを処理するモジュール(MVCではコントローラー、3層ではアプリケーション層)を分離する必要性があるということなのです。

それはなぜか?

MVCでは特に顕著ですが、
データを保存するにもデータベースなどデータを保存、管理するための取り扱い方を知っている必要があります。

データを取り扱うモジュールを分離させない場合
データを処理するモジュールが、バラバラの方法でデータを保存し始めます。

データ保存方法が分散した設計を表す図

この構成は、あらゆるモジュールが"データ保存方法"という関心事を知っているという状態になります。
これは設計原則の関心の分離に反しています

関心事が分離されていないと何が起こるのでしょうか?

一つは、変更容易性が損なわれます。
保存先のDBが変更された場合、データ保存方法を知るすべてのモジュールが修正の対象となります。

次に、凝集度が下がり、密結合を引き起こします
様々なモジュールにデータが散逸的に保持されるため、
あらゆるモジュールがあらゆる方法でそれらのデータを参照することになり
依存性が管理されないカオスなソフトウェアを生み出します。

先人の方々はこれらの経験を経て、
真っ先にデータは分離すべき対象であると定義したのだと思われます。

データクラスの概念図


dataclassesの使い方

前置きが長くなりましたが、ここからdataclassesの使い方を紹介します

dataclassesの定義

始めにdataclassesの定義方法は次の通りです。
from dataclasses import dataclass, field

@dataclass
class SampleDataClass:
    '''
    この記述が省略可能
    def __init__(self, id, name):
        self.ID = id
        self.NAME = name
    '''

    _id: int = 0
    _name: str = ''

このように__init__関数が自動生成されるため実装量が減ることが分かります

dataclassesの初期化

このデータクラスを初期化する場合は次の通りです。
data_1 = SampleDataClass(ID = 1, NAME="data_1")
print(data_1)
# SampleDataClass(ID=1, NAME='data_1')

dataclassesの参照、変更

次に、データクラスの値を変更する場合は次の通りです。
data_1.NAME = "changed_data_1"
print(data_1)
# SampleDataClass(ID=1, NAME='changed_data_1')

dataclassesを変更不可にする

場合によっては、初期化時以外に変更されたくない場合があるかと思います。
その場合はデータクラス初期化時に frozen = True を定義します
from dataclasses import dataclass, field

# frozen = Trueを追記
@dataclass(frozen = True)
class SampleDataClass:
    ID: int = 0
    NAME: str = ''


if __name__ == '__main__':
    data_1 = SampleDataClass(ID = 1, NAME="data_1")
    print(data_1)
    # SampleDataClass(ID=1, NAME='data_1')

    data_1.NAME = "changed_data_1"
    # frozen = True のため変更ができない
    # dataclasses.FrozenInstanceError: cannot assign to field 'NAME'

アーキテクチャ観点では明示的にデータの変更スコープを制限できるので
基本的には frozen = True を定義し、変更されるデータクラスである場合にのみ
frozen = Falseにしていくという設計方針が好ましいと考えられます

dataclassesのlist, dictの定義、初期化

その他メンバーにlistやdictを含める場合初期化の方法が異なるため注意が必要です
from dataclasses import dataclass, field

@dataclass(frozen = True)
class SampleDataClass:

    ID: int = 0
    NAME: str = ''
    DATA_LIST: list = field(default_factory=list)
    DATA_DICT: dict = field(default_factory=dict)

if __name__ == '__main__':
    data_1 = SampleDataClass(
        ID = 1,
        NAME="data_1",
        DATA_LIST=["A", "B", "C"],
        DATA_DICT={"key_A":"value_A"})
    print(data_1)
    # SampleDataClass(ID=1, NAME='data_1', DATA_LIST=['A', 'B', 'C'], DATA_DICT={'key_A': 'value_A'})

さいごに

Pythonは手軽に書けることもあり
細かな設計なしに実装を始めることも多いかと思います

しかし、コードが肥大化するにつれコードの管理が難しくなります
なので是非とも初めの段階からdataclassesを活用し見通しの良い実装をお勧めします

AIで副業ならココから!

まずは無料会員登録

プロフィール

メーカーで研究開発を行う現役エンジニア
組み込み機器開発や機会学習モデル開発に従事しています

本ブログでは最新AI技術を中心にソースコード付きでご紹介します


Twitter

カテゴリ

このブログを検索

ブログ アーカイブ

TeDokology