データクラス¶
型チェッカーは、dataclasses モジュールを通じて作成されたデータクラスをサポートする必要があります。 さらに、型システムには、サードパーティのクラスを標準のデータクラスのように動作させるメカニズムが含まれています。
dataclass_transform デコレーター¶
(元々 PEP 681 で指定されています。)
仕様¶
この仕様では、typing モジュールに dataclass_transform() という名前のデコレーター関数について説明します。 このデコレーターは、デコレーター自体である関数、クラス、またはメタクラスに適用できます。 dataclass_transform の存在は、デコレーター関数、クラス、またはメタクラスがクラスを変換し、データクラスのような動作を付与するランタイムの「マジック」を実行することを静的型チェッカーに伝えます。
dataclass_transform が関数に適用される場合、デコレーターとして装飾された関数を使用することは、データクラスのようなセマンティクスを適用するものと見なされます。 関数にオーバーロードがある場合、dataclass_transform デコレーターは関数の実装またはオーバーロードのいずれか 1 つに適用できますが、複数のオーバーロードには適用できません。 オーバーロードに適用される場合でも、dataclass_transform デコレーターは関数のすべての使用に影響を与えます。
dataclass_transform がクラスに適用される場合、データクラスのようなセマンティクスは、装飾されたクラスから直接または間接的に派生するクラスや、装飾されたクラスをメタクラスとして使用するクラスに対して適用されるものと見なされます。 装飾されたクラスおよびその基本クラスの属性はフィールドとは見なされません。
各アプローチの例は、以下のセクションに示されています。 各例では、CustomerModel クラスをデータクラスのようなセマンティクスで作成します。 装飾されたオブジェクトの実装は簡潔さのために省略されていますが、クラスを次のように変更するものと仮定します。
クラス内およびその親クラス内で宣言されたデータフィールドを使用して
__init__メソッドを合成します。__eq__および__ne__メソッドを合成します。
型チェッカーは、CustomerModel クラスが合成された __init__ メソッドを使用してインスタンス化できることを認識します。
# 位置引数を使用
c1 = CustomerModel(327, "John Smith")
# キーワード引数を使用
c2 = CustomerModel(id=327, name="John Smith")
# これらの呼び出しはランタイムエラーを生成し、静的型チェッカーによってエラーとしてフラグ付けされるべきです。
c3 = CustomerModel()
c4 = CustomerModel(327, first_name="John")
c5 = CustomerModel(327, "John Smith", 0)
デコレーター関数の例¶
_T = TypeVar("_T")
# ``create_model`` デコレーターはライブラリによって定義されます。
# これはタイプスタブまたはインラインで行うことができます。
@typing.dataclass_transform()
def create_model(cls: Type[_T]) -> Type[_T]:
cls.__init__ = ...
cls.__eq__ = ...
cls.__ne__ = ...
return cls
# ``create_model`` デコレーターは次のように新しいモデルクラスを作成するために使用できます。
@create_model
class CustomerModel:
id: int
name: str
クラスの例¶
# ``ModelBase`` クラスはライブラリによって定義されます。 これはタイプスタブまたはインラインで行うことができます。
@typing.dataclass_transform()
class ModelBase: ...
# ``ModelBase`` クラスは次のように新しいモデルサブクラスを作成するために使用できます。
class CustomerModel(ModelBase):
id: int
name: str
メタクラスの例¶
# ``ModelMeta`` メタクラスおよび ``ModelBase`` クラスはライブラリによって定義されます。 これはタイプスタブまたはインラインで行うことができます。
@typing.dataclass_transform()
class ModelMeta(type): ...
class ModelBase(metaclass=ModelMeta): ...
# ``ModelBase`` クラスは次のように新しいモデルサブクラスを作成するために使用できます。
class CustomerModel(ModelBase):
id: int
name: str
デコレーター関数およびクラス/メタクラスのパラメータ¶
データクラスのような機能を提供するデコレーター関数、クラス、またはメタクラスは、特定の動作を変更するパラメータを受け入れる場合があります。 この仕様では、データクラス変換によって使用される場合に静的型チェッカーが尊重しなければならない次のパラメータを定義します。 これらのパラメータは bool 引数を受け入れ、bool 値(True または False)を静的に評価できる必要があります。
eq、order、frozen、initおよびunsafe_hashは、標準ライブラリのデータクラスでサポートされているパラメータであり、その意味は PEP 557 で定義されています。kw_only、match_argsおよびslotsは、標準ライブラリのデータクラスでサポートされているパラメータであり、Python 3.10 で初めて導入されました。
dataclass_transform パラメータ¶
dataclass_transform のパラメータは、デフォルトの動作を基本的にカスタマイズするためのものです。
_T = TypeVar("_T")
def dataclass_transform(
*,
eq_default: bool = True,
order_default: bool = False,
kw_only_default: bool = False,
frozen_default: bool = False,
field_specifiers: tuple[type | Callable[..., Any], ...] = (),
**kwargs: Any,
) -> Callable[[_T], _T]: ...
eq_defaultは、呼び出し元がeqパラメータを省略した場合にTrueまたはFalseと見なすかどうかを示します。 指定されていない場合、eq_defaultは True にデフォルト設定されます(データクラスのデフォルトの仮定)。order_defaultは、呼び出し元がorderパラメータを省略した場合にTrueまたはFalseと見なすかどうかを示します。 指定されていない場合、order_defaultは False にデフォルト設定されます(データクラスのデフォルトの仮定)。kw_only_defaultは、呼び出し元がkw_onlyパラメータを省略した場合にTrueまたはFalseと見なすかどうかを示します。 指定されていない場合、kw_only_defaultは False にデフォルト設定されます(データクラスのデフォルトの仮定)。frozen_defaultは、呼び出し元がfrozenパラメータを省略した場合にTrueまたはFalseと見なすかどうかを示します。 指定されていない場合、frozen_defaultは False にデフォルト設定されます(データクラスのデフォルトの仮定)。field_specifiersは、フィールドを記述するサポートされるクラスの静的リストを指定します。 一部のライブラリは、フィールド指定子のインスタンスを割り当てる関数も提供しており、これらの関数もこのタプルに指定できます。 指定されていない場合、field_specifiersは空のタプル(フィールド指定子はサポートされていない)にデフォルト設定されます。 標準のデータクラスの動作は、Fieldと呼ばれる 1 種類のフィールド指定子と、このクラスのインスタンスを作成するヘルパー関数(field)のみをサポートするため、標準ライブラリのデータクラスの動作を説明する場合、タプル引数(dataclasses.Field, dataclasses.field)を提供します。kwargsは、dataclass_transformに任意の追加のキーワード引数を渡すことを可能にします。 これにより、型チェッカーはtyping.pyの変更を待たずに実験的なパラメータをサポートする自由を得ることができます。 型チェッカーは、認識されないパラメータに対してエラーを報告する必要があります。
将来的には、ユーザーコードで一般的な動作をサポートするために必要に応じて、dataclass_transform に追加のパラメータを追加する場合があります。 これらの追加は、追加の PEP を介さずに typing-sig での合意に達した後に行われます。
次のセクションでは、これらのパラメータの使用方法を示す追加の例を提供します。
デコレーター関数の例¶
# ``create_model`` 関数がキーワード専用パラメータを合成された ``__init__`` メソッドに仮定することを示します。 ``kw_only=False`` で呼び出されない限り。 常に順序関連のメソッドを合成し、この動作を上書きする方法を提供しません。
@typing.dataclass_transform(kw_only_default=True, order_default=True)
def create_model(
*,
frozen: bool = False,
kw_only: bool = True,
) -> Callable[[Type[_T]], Type[_T]]: ...
# このデコレーターがこのライブラリからインポートされたコードによってどのように使用されるかの例:
@create_model(frozen=True, kw_only=False)
class CustomerModel:
id: int
name: str
クラスの例¶
# このクラスから派生するクラスが比較メソッドを合成することをデフォルトとすることを示します。
@typing.dataclass_transform(eq_default=True, order_default=True)
class ModelBase:
def __init_subclass__(
cls,
*,
init: bool = True,
frozen: bool = False,
eq: bool = True,
order: bool = True,
):
...
# このクラスがこのライブラリからインポートされたコードによってどのように使用されるかの例:
class CustomerModel(
ModelBase,
init=False,
frozen=True,
eq=False,
order=False,
):
id: int
name: str
メタクラスの例¶
# このメタクラスを使用するクラスが比較メソッドを合成することをデフォルトとすることを示します。
@typing.dataclass_transform(eq_default=True, order_default=True)
class ModelMeta(type):
def __new__(
cls,
name,
bases,
namespace,
*,
init: bool = True,
frozen: bool = False,
eq: bool = True,
order: bool = True,
):
...
class ModelBase(metaclass=ModelMeta):
...
# このクラスがこのライブラリからインポートされたコードによってどのように使用されるかの例:
class CustomerModel(
ModelBase,
init=False,
frozen=True,
eq=False,
order=False,
):
id: int
name: str
フィールド指定子¶
データクラスのようなセマンティクスをサポートするほとんどのライブラリは、クラス定義がクラス内の各フィールドに関する追加のメタデータを提供できるようにする 1 つ以上の「フィールド指定子」タイプを提供します。 このメタデータは、たとえばデフォルト値を記述したり、フィールドが合成された __init__ メソッドに含まれるかどうかを示したりすることができます。
追加のメタデータが必要ない場合、フィールド指定子を省略できます。
@dataclass
class Employee:
# フィールド指定子なしのフィールド
name: str
# フィールド指定子クラスインスタンスを使用するフィールド
age: Optional[int] = field(default=None, init=False)
# デフォルト値を記述するための型注釈と単純な初期化子を持つフィールド
is_paid_hourly: bool = True
# 型注釈が提供されていないため、フィールドではなく(クラス変数)
office_number = "unassigned"
フィールド指定子のパラメータ¶
データクラスのようなセマンティクスをサポートし、フィールド指定子クラスをサポートするライブラリは、通常、これらのフィールド指定子を構築するために共通のパラメータ名を使用します。 この仕様では、静的型チェッカーが理解しなければならないパラメータの名前と意味を正式化します。 これらの標準化されたパラメータはキーワード専用でなければなりません。
これらのパラメータは、compare や hash など、型チェックに影響を与えないものを除く、dataclasses.field() でサポートされているもののスーパーセットです。
フィールド指定子クラスは、コンストラクタ内で他のパラメータを使用することができ、これらのパラメータは位置指定であり、他の名前を使用することができます。
initは、フィールドが合成された__init__メソッドに含まれるかどうかを示すオプションの bool パラメータです。 指定されていない場合、initは True にデフォルト設定されます。 フィールド指定子関数は、リテラル bool 値型(Literal[False]またはLiteral[True])を使用してinitの値を暗黙的に指定するオーバーロードを使用できます。defaultは、フィールドのデフォルト値を提供するオプションのパラメータです。default_factoryは、フィールドのデフォルト値を返すランタイムコールバックを提供するオプションのパラメータです。defaultまたはdefault_factoryのいずれも指定されていない場合、フィールドにはデフォルト値がないと見なされ、クラスのインスタンス化時に値を提供する必要があります。factoryはdefault_factoryのエイリアスです。 標準ライブラリのデータクラスはdefault_factoryという名前を使用しますが、attrs は多くのシナリオでfactoryという名前を使用するため、このエイリアスは attrs をサポートするために必要です。kw_onlyは、フィールドがキーワード専用としてマークされるかどうかを示すオプションの bool パラメータです。 True の場合、フィールドはキーワード専用になります。 False の場合、キーワード専用にはなりません。 指定されていない場合、dataclass_transformで装飾されたオブジェクトのkw_onlyパラメータの値が使用されます。 それも指定されていない場合、dataclass_transformのkw_only_defaultの値が使用されます。aliasは、フィールドの代替名を提供するオプションの str パラメータです。 この代替名は、合成された__init__メソッドで使用されます。converterは、フィールドに値を割り当てる際に使用される呼び出し可能なオプションのパラメータです。
default、default_factory および factory のいずれかを複数指定することはエラーです。
次の例は上記を示しています。
# ライブラリコード(タイプスタブまたはインライン内)
# このライブラリでは、リゾルバを渡すと init は False でなければならず、Literal[False] を持つオーバーロードがそれを強制します。
@overload
def model_field(
*,
default: Optional[Any] = ...,
resolver: Callable[[], Any],
init: Literal[False] = False,
) -> Any: ...
@overload
def model_field(
*,
default: Optional[Any] = ...,
resolver: None = None,
init: bool = True,
) -> Any: ...
@typing.dataclass_transform(
kw_only_default=True,
field_specifiers=(model_field, ))
def create_model(
*,
init: bool = True,
) -> Callable[[Type[_T]], Type[_T]]: ...
# このライブラリからインポートされたコード:
@create_model(init=False)
class CustomerModel:
id: int = model_field(resolver=lambda : 0)
name: str
ランタイムの動作¶
ランタイムでは、dataclass_transform デコレーターの唯一の効果は、装飾された関数またはクラスに __dataclass_transform__ という名前の属性を設定してインスペクションをサポートすることです。 属性の値は、dataclass_transform パラメータの名前をその値にマッピングする辞書である必要があります。
たとえば:
{
"eq_default": True,
"order_default": False,
"kw_only_default": False,
"field_specifiers": (),
"kwargs": {}
}
データクラスのセマンティクス¶
明示的に述べられていない限り、dataclass_transform で影響を受けるクラスは、dataclass_transform で装飾されたクラスを継承するか、dataclass_transform で装飾された関数で装飾されることによって、標準ライブラリの dataclass() のように動作するものと見なされます。
これには、次のセマンティクスが含まれますが、これに限定されません。
凍結されたデータクラスは、非凍結データクラスを継承できません。
dataclass_transformで装飾されたクラスは、凍結されているとも非凍結されているとも見なされないため、凍結されたクラスがそれを継承することができます。 同様に、dataclass_transformで装飾されたメタクラスを直接指定するクラスは、凍結されているとも非凍結されているとも見なされません。次のクラスの例を考えてみましょう。
# ModelBase は ``dataclass_transform`` で装飾されているため、「凍結」または「非凍結」と見なされません。 @typing.dataclass_transform() class ModelBase(): ... # Vehicle は「frozen=True」を指定していないため、非凍結と見なされます。 class Vehicle(ModelBase): name: str # Car は凍結されたクラスであり、非凍結クラスである Vehicle から派生しています。 これはエラーです。 class Car(Vehicle, frozen=True): wheel_count: int
そして、これらの類似したメタクラスの例:
@typing.dataclass_transform() class ModelMeta(type): ... # ModelBase は、ModelMeta をメタクラスとして直接指定しているため、「凍結」または「非凍結」と見なされません。 class ModelBase(metaclass=ModelMeta): ... # Vehicle は「frozen=True」を指定していないため、非凍結と見なされます。 class Vehicle(ModelBase): name: str # Car は凍結されたクラスであり、非凍結クラスである Vehicle から派生しています。 これはエラーです。 class Car(Vehicle, frozen=True): wheel_count: int
フィールドの順序と継承は、Python ドキュメント で指定されたルールに従うものと見なされます。 これには、オーバーライドの影響(親クラスですでに定義されているフィールドを子クラスで再定義すること)が含まれます。
PEP 557 は、デフォルト値のないすべてのフィールドがデフォルト値のあるフィールドの前に表示される必要があることを示しています。 PEP 557 では明示的に述べられていませんが、このルールは
init=Falseの場合には無視され、この仕様でもその状況ではこの要件を無視します。 同様に、__init__のキーワード専用パラメータが使用される場合、この順序を強制する必要はないため、kw_onlyセマンティクスが有効な場合、このルールは強制されません。dataclassと同様に、クラス内で明示的に宣言されたメソッドがある場合、メソッドの合成はスキップされます。 基本クラスのメソッド宣言は、メソッドの合成をスキップする原因にはなりません。たとえば、クラスが
__init__メソッドを明示的に宣言している場合、そのクラスには__init__メソッドは合成されません。KW_ONLY センチネル値は、Python ドキュメント および bpo-43532 で説明されているようにサポートされています。
ClassVar 属性はデータクラスフィールドとは見なされず、データクラスメカニズムによって無視されます。
データクラスフィールドは
Final[...]で注釈を付けることができます。 たとえば、データクラス本体内のx: Final[int]は、合成された__init__メソッドで初期化され、その後は代入できないデータクラスフィールドxを指定します。 クラス本体内で初期化されたFinalデータクラスフィールドは、明示的にClassVarで注釈されない限り、クラス属性ではありません。 たとえば、x: Final[int] = 3は、合成された__init__メソッドでデフォルト値3を持つデータクラスフィールドxです。 データクラスの最終クラス変数は、たとえばx: ClassVar[Final[int]] = 3のように明示的に注釈を付ける必要があります。
コンバーター¶
converter パラメータは、関連する属性に値を割り当てる際に使用される呼び出し可能なものを提供するためにフィールド定義で指定できます。 この機能により、属性の割り当て中に自動的な型変換と検証が可能になります。
コンバーターの動作:
コンバーターは、デフォルト値の割り当て、合成された
__init__メソッドでの割り当て、および直接の属性設定(例:obj.attr = value)を含むすべての属性割り当てに使用されます。コンバーターは属性の読み取り時には使用されません。属性はすでに変換されているはずです。
コンバーターの型付けルール:
converterは、単一の位置引数を受け入れる呼び出し可能なものでなければなりません(ただし、他のオプションの引数を受け入れることができますが、型付け目的では無視されます)。最初の位置引数の型は、フィールドの合成された
__init__パラメータの型を提供します。呼び出し可能なものの戻り値の型は、フィールドの宣言された型に代入可能でなければなりません。
defaultまたはdefault_factoryが提供されている場合、デフォルト値の型はconverterの最初の位置引数に代入可能でなければなりません。
使用例:
def str_or_none(x: Any) -> str | None:
return str(x) if x is not None else None
@custom_dataclass
class Example:
int_field: int = custom_field(converter=int)
str_field: str | None = custom_field(converter=str_or_none)
path_field: pathlib.Path = custom_field(
converter=pathlib.Path,
default="default/path.txt"
)
# 使用例
example = Example("123", None, "some/path")
# example.int_field == 123
# example.str_field == None
# example.path_field == pathlib.Path("some/path")
未定義の動作¶
複数の dataclass_transform デコレーターが見つかった場合、単一の関数(そのオーバーロードを含む)、単一のクラス、またはクラス階層内で、結果の動作は未定義です。 ライブラリの作成者はこれらのシナリオを避けるべきです。