LoginSignup
717
569

More than 5 years have passed since last update.

Python3.7からは「Data Classes」がクラス定義のスタンダードになるかもしれない

Last updated at Posted at 2018-05-20

はじめに

Python3.7が2018/06/15にリリースされる予定です。
https://www.python.org/dev/peps/pep-0537/#release-schedule

Python3.7の新機能に Data Classes がありますが、これを使いこなせばクラス定義が楽になりそうです。

尚、Python3.7の環境作成は以下を参照して下さい。

もうすぐリリースされるPython3.7環境をDockerで作る - Qiita

Data Classes とは

データを格納するためのクラスを簡単に定義できる機能です。
クラス定義にデコレータを1つ付けるだけで__init____str__などの特殊メソッドを自動生成してくれます。

基本的な使い方

  • クラス定義にdataclassデコレータを付ける
  • クラス変数でフィールドを定義する
クラスの定義
import dataclasses

@dataclasses.dataclass
class User:
    name: str
    age: int = 0
クラスを使う
User('tarou', 99)
Out[3]: User(name='tarou', age=99)

User('hanako')
Out[4]: User(name='hanako', age=0)

user1 = User('tarou')
user1.age = 99
user1.name, user1.age
Out[5]: ('tarou', 99)

user1 == User('hanako', 99)
Out[6]: False

user1 == User('tarou', 99)
Out[7]: True

カスタマイズ

メソッドを定義する

通常のクラスと同じようにメソッドを定義できます。

import dataclasses

@dataclasses.dataclass
class User:
    name: str
    age: int = 0

    def format(self):
        return f'{self.name}さん({self.age}歳)'

イミュータブルにする

デフォルトではミュータブル(変更可能)なオブジェクトになりますが、frozen=Trueにするとイミュータブル(変更不可)なオブジェクトになります。

@dataclasses.dataclass(frozen=True)
class User:
    name: str
    age: int = 0

user1 = User('tarou')
user1.age = 99

FrozenInstanceError                       Traceback (most recent call last)
<ipython-input-60-cba1cafa11e3> in <module>()
----> 1 user1.age = 99

<string> in __setattr__(self, name, value)

FrozenInstanceError: cannot assign to field 'age'

ミュータブルなデフォルト値を使う

@dataclasses.dataclass
class User:
    name: str
    age: int = 0
    items: List[int] = dataclasses.field(default_factory=list)

User('tarou')

Out[12]: User(name='tarou', age=0, items=[])

フィールドを比較対象から除外する

@dataclasses.dataclass
class User:
    name: str
    age: int = dataclasses.field(default=0, compare=False)

User('tarou', 10) == User('tarou', 20)
Out[15]: True

フィールドを__init__の引数から除外する

@dataclasses.dataclass
class User:
    name: str
    age: int = dataclasses.field(default=0, init=False)

User('tarou')

Out[19]: User(name='tarou', age=0)

User('tarou', age=99)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-20-ab6e147bf66a> in <module>()
----> 1 User('tarou', age=99)

TypeError: __init__() got an unexpected keyword argument 'age'

自動生成された__init__の後に処理を入れる

__post_init__という名前のメソッドを定義すると__init__の後に呼ばれます。

@dataclasses.dataclass
class User:
    name: str
    age: int = 0
    def __post_init__(self):
        print('__post_init__', self.name, self.age)

User('tarou')
__post_init__ tarou 0
Out[24]: User(name='tarou', age=0)

__init__のみで使用する変数を指定する

クラス変数で型をdataclasses.InitVarにすると、__init__でのみ使用するパラメータになります。
dataclasses.InitVarで定義したクラス変数はフィールドとは認識されずインスタンスには保持されません。

@dataclasses.dataclass
class User:
    name: str = dataclasses.field(init=False)
    age: int = dataclasses.field(init=False)
    values: dataclasses.InitVar[Tuple[str, int]] = None
    def __post_init__(self, values):
        self.name, self.age = values

User(values=('tarou', 99))

Out[28]: User(name='tarou', age=99)

ユーティリティ関数

フィールドリストを取得する

dataclasses.fields(User)

Out[22]:
(Field(name='name',type=<class 'str'>,default=<dataclasses._MISSING_TYPE object at 0x7fd864bfc080>,default_factory=<dataclasses._MISSING_TYPE object at 0x7fd864bfc080>,init=True,repr=True,hash=None,compare=True,metadata={}),
 Field(name='age',type=<class 'int'>,default=0,default_factory=<dataclasses._MISSING_TYPE object at 0x7fd864bfc080>,init=False,repr=True,hash=None,compare=True,metadata={}))

dictに変換する

dataclasses.asdict(User('tarou', 99))

Out[8]: {'name': 'tarou', 'age': 99}

tupleに変換する

dataclasses.astuple(User('tarou', 99))

Out[9]: ('tarou', 99)

最後に

基本的な使い方だけだと namedtuple と大きな違いはないのですが、Data Classes では色々とカスタマイズができることがわかりました。
今後クラス定義は Data Classes を使うのが標準的になりそうな予感がします。

717
569
2

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
717
569