web-dev-qa-db-ja.com

python dataclass __init__メソッドで型変換を強制する

次の非常に単純なデータクラスがあります。

import dataclasses

@dataclasses.dataclass
class Test:
    value: int

クラスのインスタンスを作成しますが、整数の代わりに文字列を使用します。

>>> test = Test('1')
>>> type(test.value)
<class 'str'>

私が実際に欲しいのは、クラス定義で定義されたデータ型iへの強制変換です。

>>> test = Test('1')
>>> type(test.value)
<class 'int'>

__init__メソッドを手動で記述する必要がありますか、またはこれを達成する簡単な方法はありますか?

8
johnson

データクラス属性のタイプヒントは、タイプが強制またはチェックされるという意味では決して守られません。 mypy のようなほとんどの静的型チェッカーはこの仕事をすることが期待されており、Pythonは実行されないので、実行時にそれを行いません。

手動の型チェックコードを追加する場合は、 ___post_init___ メソッドで追加します。

_@dataclasses.dataclass
class Test:
    value: int

    def __post_init__(self):
        if not isinstance(self.value, int):
            raise ValueError('value not an int')
            # or self.value = int(self.value)
_

dataclasses.fields(self) を使用して、フィールドとタイプを指定する Field オブジェクトのタプルを取得し、これをループすることができますフィールドごとに個別に書き込むことなく、フィールドごとに自動的に。

_def __post_init__(self):
    for field in dataclasses.fields(self):
        value = getattr(self, field.name)
        if not isinstance(value, field.type):
            raise ValueError(f'Expected {field.name} to be {field.type}, '
                             f'got {repr(value)}')
            # or setattr(self, field.name, field.type(value))
_
11
deceze

これは、__post_init__メソッドを使用して実現できます。

import dataclasses

@dataclasses.dataclass
class Test:
    value : int

    def __post_init__(self):
        self.value = int(self.value)

このメソッドは__init__メソッドの後に呼び出されます

https://docs.python.org/3/library/dataclasses.html#post-init-processing

2
Merig

ええ、簡単な答えは、自分自身の__init__()で変換を自分で行うことです。これは、オブジェクトが必要なためですfrozen=True

型検証については、Pydandicはそれを行うと主張していますが、まだ試していません: https://pydantic-docs.helpmanual.io/

0
Lars P