Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

はじめに

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__などの特殊メソッドを自動生成してくれます。

https://docs.python.org/ja/3.7/library/dataclasses.html

基本的な使い方

  • クラス定義に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 を使うのが標準的になりそうな予感がします。

tag1216
Qiita戦闘力はキュイレベルです! /作ったもの ◆QiiTrend:https://qiitrend.herokuapp.com/ ◆Qiiner:https://qiiner.tag1216.net/ ◆Qiitaでいいねしたら草生えるページ:https://qiiner.tag1216.net/likes-heatmap
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした