初めに
dataclassはデータを格納するためのクラスです。通常のクラスとの違いや基本的な使い方をまとめています。
Index
dataclass
dataclassとは
dataclassとは、データを格納するためのクラスです。dataclasses
モジュールで提供されます。
dataclassとclassの比較
同じ内容のPerson
クラスを通常のclassとdataclassで作成してみます。
(通常のclass)
class Person: def __init__(self, name, age): self.name = name self.age = age def __repr__(self): return f"Person(name={self.name}, age={self.age})" def __eq__(self, other): return self.name == other.name and self.age == other.age
(dataclass)
from dataclasses import dataclass @dataclass class Person: name: str age: int
このコードを見てわかる通り、dataclassには次のような有用性があります。
- コードを簡潔に書くことができる
__init__
や__repr__
など特殊メソッドが自動生成される- 型アノテーションを使用できる
※型アノテーション:変数や関数の引数・戻り値に期待されるデータ型を明示的に記述すること
使い方
基本的な使い方
from dataclasses import dataclass @dataclass class Person: name: str age: int person = Person("山田", 23) print(f"{person.name}, {person.age}") # 出力 : 山田, 23
dataclasses
モジュールからdataclass
をインポートし、使用します。dataclassを作成するには、class
の上に@dataclass
デコレータを記述します。
デフォルト値
フィールドには、デフォルト値を設定することもできます。
from dataclasses import dataclass @dataclass class Person: name: str age: int = 20 # デフォルト値を設定 # オブジェクトの生成 person1 = Person("田中") person2 = Person("中澤", 32) # 確認 print(f"{person1.name} は {person1.age} 歳") # 田中 は 20 歳 print(f"{person2.name} は {person2.age} 歳") # 中澤 は 32 歳
引数を記述しない場合はデフォルト値が参照され、引数を記述した場合はその値が参照されます。
注意点として、デフォルト値を設定するフィールドは、デフォルト値を設定しないフィールドの後に記述する必要があります。
from dataclasses import dataclass @dataclass class Person: name: str = "田中" age: int # これはエラー:デフォルト値を設定しているフィールドを前に記述されている
ミュータブルな型(list、dict、set)の場合は注意が必要です。下の例のように参照元が同じになってしまい、意図していない処理をしてしまう可能性があります。
(例:二つのオブジェクトで参照元が同じ)
from dataclasses import dataclass @dataclass class Person: friends: list = [] # デフォルト値として空のリストを設定 # オブジェクトの生成 person1 = Person() person2 = Person() # person1.friendsとperson2.friendsは同じオブジェクトを参照 person1.friends.append("John") print(person2.friends) # ['John']と出力
これを解決するには、field()
関数を用います。
from dataclasses import dataclass, field @dataclass class Person: friends: list[str] = field(default_factory=list) # オブジェクトの生成 person1 = Person() person2 = Person() # person1.friendsとperson2.friendsは異なるオブジェクト person1.friends.append("John") print(person1.friends) # 出力 : ['John'] print(person2.friends) # 出力 : []
今回は、list
のデフォルト値を設定するため、field(default_factory=list)
としています。辞書の場合はfield(default_factory=dict)
、集合の場合はfield(default_factory=set)
とします。
また、中身があるものをデフォルト値にする場合、次のように中身を返す関数をdefault_factory
に指定します。
from dataclasses import dataclass, field from typing import Dict def get_default_address() -> Dict[str, str]: return {"city": "東京", "prefecture": "東京都"} @dataclass class Person: address: Dict[str, str] = field(default_factory=get_default_address) # オブジェクトの生成 person1 = Person() # addressフィールドは新しいオブジェクトとして生成される print(person1.address) # {'city': '東京', 'prefecture': '東京都'}
関数は無名関数(lambda
を用いて記述する)を用いてもよいです。
@dataclass class Person: address: Dict[str, str] = field(default_factory=lambda: {"city": "東京", "prefecture": "東京都"})
==による処理
classの場合とdataclassの場合で==
による処理が異なります。
型 | 比較する対象 |
---|---|
class | オブジェクトの識別子(メモリ上のアドレス)を比較 |
dataclass | フィールドの値を比較 |
classの場合、オブジェクトの識別子を比較するため、クラス内のフィールドの値が全く同じ場合でもインスタンスが別であれば、Falseとなります。
dataclassの場合、デフォルトで__eq__()
メソッドが自動生成されるため、フィールドの値を比較します。すべてのフィールドが等しい場合、Trueとなります。
class Person: def __init__(self, name, age): self.name = name self.age = age person1 = Person("John", 20) person2 = Person("John", 20) # オブジェクトの識別子を比較 print(person1 == person2) # False
from dataclasses import dataclass @dataclass class Person: name: str age: int person1 = Person("John", 20) person2 = Person("John", 20) # フィールドの値を比較 print(person1 == person2) # True
frozon
dataclassの引数には、frozon
があります。これはデフォルトではFalseになっていますが、Trueにすることで、読み込み専用のインスタンスを生成することができます。
from dataclasses import dataclass, field @dataclass(frozen=True) class Test: subject: str point: int # オブジェクトの生成 test1 = Test("国語", 88) test1.subject = "数学" #エラー : dataclasses.FrozenInstanceError: cannot assign to field 'subject'
asdict()、astuple()
asdict()
関数はデータクラスを辞書に、astuple()
関数はデータクラスをタプルに変換します。
(asdict()
関数)
from dataclasses import dataclass, asdict @dataclass class Person: name: str age: int person = Person("John", 20) # カスタムdict_factoryを使用 person_dict = asdict(person) # 辞書の内容を確認 print(person_dict) # {'name': 'John', 'age': 20}
(astuple()
関数)
from dataclasses import dataclass, astuple @dataclass class Person: name: str age: int person = Person("John", 20) # データクラスをタプルに変換 person_tuple = astuple(person) # タプルの内容を確認 print(person_tuple) # ('John', 20)