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