PEP681 - Data Class Transformsって何のためにあるの?
この記事はMapbox Japan Advent Calendar 2022 8日目の記事です。
PEP681が追加された背景
PEP681 - Data Class Transformsはtypingモジュールに@dataclass_transformというデコレータを追加する変更でpydanticやSQLAlchemy、pyserde等のdataclassをベースにしたライブラリの型チェックし易くするために提案されました。
例えば、@dataclassを追加するだけの超シンプルなデコレータ@add_dataclassがあるとします。
from dataclasses import dataclass
from typing import Type, TypeVar
def add_dataclass(cls: Type[T]) -> Type[T]:
""" @dataclassを追加するだけのデコレータ """
dataclass(cls)
return cls
@add_dataclass
class A:
v: int
a = A(10)
print(a)
このコードを実行すると意図した通りに動いてくれるんですが、
$ python a.py
A(v=10)
mypyで型チェックをすると、以下のようにエラーが出てしまいます。
$ mypy a.py
a.py:15: error: Too many arguments for "A"
なんでmypyが正しくチェックしてくれないかというと、タイプチェッカーはdataclassを解釈して型アノテーションに基づいてコンストラクタ等のメソッドの型チェックをしてくれるんですが、実際にPythonコードを実行している訳ではないのでadd_dataclassの内部でdataclassが呼ばれようが型チェックをしてくれません。タイプチェッカー的にはdataclassデコレータは付いてないことになるので上記の型エラーが出力されるわけです。
PEP681ができる前まではどうしてた?
じゃあ、PEP681ができる前まではどうしていたかというと、
1. 必ず@dataclassデコレータを付けるようにする
@add_dataclass
@dataclass
class A:
v: int
この例だとdataclassを付けるだけのデコレータにさらに@dataclass付けているので意味がなくなってしまいますが、タイプチェッカーのエラーはなくなります。
pyserdeはこのアプローチをとっていて、本来@dataclassがなくても機能しますが、付けることが推奨されています。
@serde
@dataclass # <= 必須ではないが推奨
class Foo:
...
2. typing.TYPE_CHECKINGでタイプチェッカーを騙す
typing.TYPE_CHECKINGという型結チェック時だけTrueになる変数があるので、以下のようにadd_dataclassがdataclassであるかのようにエイリアスを作るとタイプチェッカーを騙すことができます。
if TYPE_CHECKING:
from dataclasses import dataclass as add_dataclass
else:
def add_dataclass(cls: Type[T]) -> Type[T]:
dataclass(cls)
return cls
これでエラーが出なくなりました。
$ mypy a.py
Success: no issues found in 1 source file
確かpydanticもこのアプローチだったと思います。
PEP681
Python 3.11で追加されたtyping.datalass_transformを使えば、もっと簡単に解決できるようになります。 @typing.dataclass_transform()をデコレータ関数に付けるだけ。
from typing import Type, TypeVar, dataclass_transform
@dataclass_transform()
def add_dataclass(cls: Type[T]) -> Type[T]:
""" @dataclassを追加するだけのデコレータ """
dataclass(cls)
return cls
Python 3.7~3.10の場合は、type_extensions>=4.1.0にバックポートがあるのでそちらを使いましょう。
タイプチェッカーのPEP681サポート状況
が、残念ながら実はmypyは2022年11月11日現在でPEP681をサポートしていません 🥹
主なタイプチェッカーのPEP681サポート状況は以下の通り
| mypy 0.990 | pyright 1.1279 | pytype 2022.11.10 | pyre 0.9.17 |
|---|---|---|---|
| 未対応 | 対応 | 未対応 | 未対応 |
唯一対応しているpyrightで型チェックをするとエラーは何も出なくなりました 🎉
$ pyright a.py
0 errors, 0 warnings, 0 informations
宣伝
dataclassにserdeデコレータを付けるだけで、Toml,Yaml,JSON,MsgPack等の様々なフォーマットに(de)serializeできるライブラリpyserdeを開発しています。よかったら使ってみてください 🙏
また、pyserdeでPEP681に対応した時のPRはこちらになります。参考にしてください。
Discussion