"dataclasses" --- データクラス
******************************

**ソースコード:** Lib/dataclasses.py

======================================================================

このモジュールは、"__init__()" や "__repr__()" のような *特殊メソッド*
を生成し、ユーザー定義のクラスに自動的に追加するデコレーターや関数を提
供します。このモジュールは元々、 **PEP 557** で説明されていました。

これらの生成されたメソッドで利用されるメンバー変数は **PEP 526** 型ア
ノテーションを用いて定義されます。例えば、このコードでは:

   from dataclasses import dataclass

   @dataclass
   class InventoryItem:
       """在庫内の品目を管理するクラス。"""
       name: str
       unit_price: float
       quantity_on_hand: int = 0

       def total_cost(self) -> float:
           return self.unit_price * self.quantity_on_hand

とりわけ、以下のような "__init__()" が追加されます:

   def __init__(self, name: str, unit_price: float, quantity_on_hand: int = 0):
       self.name = name
       self.unit_price = unit_price
       self.quantity_on_hand = quantity_on_hand

このメソッドは自動的にクラスに追加される点に留意して下さい。上記の
"InventoryItem" クラスの定義中にこのメソッドが直接明記されるわけではあ
りません。

Added in version 3.7.


モジュールの内容
================

@dataclasses.dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False)

   この関数は、後述する *特殊メソッド* を生成し、クラスに追加する
   *decorator* です。

   "@dataclass" デコレータは、"field" を探すためにクラスを検査します。
   "field" は *型アノテーション* を持つクラス変数として定義されます。
   後述する２つの例外を除き、 "@dataclass" は変数アノテーションで指定
   した型を検査しません。

   生成されるすべてのメソッドの中でのフィールドの順序は、それらのフィ
   ールドがクラス定義に現れた順序です。

   "@dataclass" デコレータは、後述する様々な "ダンダー" メソッド (訳注
   ：dunderはdouble underscoreの略で、メソッド名の前後にアンダースコア
   が2つ付いているメソッド) をクラスに追加します。クラスに既にこれらの
   メソッドが存在する場合の動作は、後述する引数によって異なります。デ
   コレータは呼び出した際に指定したクラスと同じクラスを返します。新し
   いクラスは生成されません。

   "@dataclass" をパラメータなしの単純なデコレータとして使用すると、こ
   のシグネチャに記載されたデフォルト値を指定した場合と同じ動作になり
   ます。 つまり、以下の 3 通りの "@dataclass" の使い方は等価です。

      @dataclass
      class C:
          ...

      @dataclass()
      class C:
          ...

      @dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False,
                 match_args=True, kw_only=False, slots=False, weakref_slot=False)
      class C:
          ...

   "@dataclass" のパラメータは以下のとおりです:

   * *init*: 真の場合 (デフォルト)、"__init__()" メソッドを生成します
     。

     クラスがすでに "__init__()" を定義している場合、このパラメータは
     無視されます。

   * *repr*: 真の場合 (デフォルト)、"__repr__()" メソッドを生成します
     。 生成される repr 文字列にはクラス名と、クラスで定義した順に各フ
     ィールドの名前と repr が含まれます。 repr から除外すると指定され
     たフィールドは含まれません。 例えば、
     "InventoryItem(name='widget', unit_price=3.0,
     quantity_on_hand=10)" となります。

     クラスがすでに "__repr__()" を定義している場合、このパラメータは
     無視されます。

   * *eq*: 真の場合 (デフォルト)、"__eq__()" メソッドを生成します。

     このメソッドは各フィールドを順に比較することでこのクラスのインス
     タンスを比較します。 比較する両方のインスタンスは、まったく同じ型
     でなければなりません。

     クラスがすでに "__eq__()" を定義している場合、このパラメータは無
     視されます。

     バージョン 3.13 で変更: 生成される "__eq__" メソッドは、以前のバ
     ージョンのようにフィールドのタプルを比較する代わりに、各フィール
     ドを個別に比較するようになりました (例えば "self.a == other.a and
     self.b == other.b")。この変更によって比較が高速になりますが、属性
     が同一性では等しくても値では等しくない場合 ("float('nan')" など)
     、結果が変わる可能性があります。Python 3.12 以前では、フィールド
     のタプルを作成し、それらを比較していました (例えば "(self.a,
     self.b) == (other.a, other.b)")。

   * *order*: 真の場合 (デフォルトは "False")、"__lt__()"、"__le__()"
     、"__gt__()"、"__ge__()" メソッドを生成します。 これらのメソッド
     は、フィールドを順に並べたタプルのようにこのクラスのインスタンス
     を比較します。 比較する両方のインスタンスは、まったく同じ型でなけ
     ればなりません。 *order* が真で *eq* が偽の場合は、"ValueError"
     を送出します。

     クラスがすでに "__lt__()"、"__le__()"、"__gt__()"、"__ge__()" の
     いずれかを定義している場合は、"TypeError" を送出します。

   * *unsafe_hash*: 真の場合、安全でない可能性があっても、
     "dataclasses" に "__hash__()" メソッドを強制的に作成させます。 そ
     れ以外の場合、*eq* と *frozen* の設定に従って "__hash__()" メソッ
     ドを生成します。 デフォルト値は "False" です。

     "__hash__()" は組み込みの "hash()" で使用されるほか、辞書や集合の
     ようなハッシュを使用するコレクションにオブジェクトを追加する際に
     も使用されます。 "__hash__()" があることは、そのクラスのインスタ
     ンスが不変であることを示唆します。 可変性は複雑な性質で、プログラ
     マの意図、"__eq__()" の有無と動作、"@dataclass" デコレータの *eq*
     と *frozen* フラグの値に依存します。

     デフォルトでは、安全な場合を除き、"@dataclass" は "__hash__()" メ
     ソッドを暗黙的に追加しません。 また、明示的に定義済みの
     "__hash__()" メソッドを追加したり変更したりしません。 クラス属性
     を "__hash__ = None" に設定することには、"__hash__()" のドキュメ
     ントで説明されているように、Python における特定の意味があります。

     "__hash__()" が明示的に定義されていないか、"None" に設定されてい
     る場合、"@dataclass" は暗黙的な "__hash__()" メソッドを追加する *
     ことがあります*。 推奨されませんが、"unsafe_hash=True" を指定して
     、"@dataclass" に "__hash__()" メソッドを強制的に作成させることが
     できます。 論理的には不変でも変更可能なクラスの場合に、この方法を
     使うことが考えられます。 これは特殊な用途であり、慎重に検討する必
     要があります。

     "__hash__()" メソッドの暗黙的な作成には、以下の規則が適用されます
     。 データクラスに明示的な "__hash__()" メソッドを定義し、同時に
     "unsafe_hash=True" を設定することはできません。 この場合は
     "TypeError" が発生します。

     *eq* と *frozen* がともに真の場合、デフォルトでは "@dataclass" は
     "__hash__()" メソッドを生成します。 *eq* が真で *frozen* が偽の場
     合、"__hash__()" は "None" に設定され、ハッシュ不可能であることを
     示します (可変なので実際にハッシュ不可能です)。 *eq* が偽の場合、
     "__hash__()" は変更されないため、スーパークラスの "__hash__()" メ
     ソッドが使用されます (スーパークラスが "object" の場合、id に基づ
     くハッシュが使われます)。

   * *frozen*: 真の場合 (デフォルトは "False")、フィールドへの代入で例
     外が発生します。 これにより、読み取り専用の凍結されたインスタンス
     をエミュレートします。 後述の 説明 を参照してください。

     クラスで "__setattr__()" または "__delattr__()" が定義されており
     、*frozen* が真の場合、"TypeError" を送出します。

   * *match_args*: 真の場合 (デフォルトは "True")、生成される
     "__init__()" メソッドのキーワード専用でないパラメータのリストから
     "__match_args__" タプルを作成します ("__init__()" が生成されない
     場合も同様です。 上記を参照)。 偽の場合、またはクラスに
     "__match_args__" がすでに定義されている場合は、"__match_args__"
     を生成しません。

      Added in version 3.10.

   * *kw_only*: 真の場合 (デフォルト値は "False")、すべてのフィールド
     をキーワード専用に指定します。 フィールドをキーワード専用にするこ
     との唯一の効果は、そのフィールドから生成される "__init__()" のパ
     ラメータを、"__init__()" の呼び出し時にキーワードで指定する必要が
     あるという点です。 詳細は用語集の *parameter* を参照してください
     。 "KW_ONLY" の節も参照してください。

     キーワード専用フィールドは "__match_args__" に含まれません。

      Added in version 3.10.

   * *slots*: 真の場合 (デフォルトは "False")、"__slots__" 属性を生成
     し、元のクラスの代わりに新しいクラスを返します。 クラスに
     "__slots__" がすでに定義されている場合は、"TypeError" を送出しま
     す。

      警告:

        "slots=True" を使用する際に基底クラスの "__init_subclass__()"
        にパラメータを渡すと、"TypeError" が発生します。 回避するには
        、パラメータなしの "__init_subclass__" を使用するか、デフォル
        ト値を使用してください。 詳細は gh-91126 を参照してください。

      Added in version 3.10.

      バージョン 3.11 で変更: 基底クラスの "__slots__" にフィールド名
      がすでに含まれている場合、オーバーライド を防ぐため、生成される
      "__slots__" には含まれません。 したがって、データクラスのフィー
      ルド名を取得するために "__slots__" を使用しないでください。 代わ
      りに "fields()" を使用してください。 継承するスロットを判定でき
      るように、基底クラスの "__slots__" には任意のイテラブルを使用で
      きますが、イテレータは使用 *できません*。

   * *weakref_slot*: 真の場合 (デフォルトは "False")、インスタンスを "
     弱参照可能" にするために必要な "__weakref__" という名前のスロット
     を追加します。 "slots=True" を指定せずに "weakref_slot=True" を指
     定するとエラーになります。

      Added in version 3.11.

   "フィールド" には、通常の Python の文法でデフォルト値を指定できます
   。

      @dataclass
      class C:
          a: int       # 'a' にはデフォルト値がない
          b: int = 0   # 'b' にデフォルト値を割り当てる

   この例では、"a" と "b" の両方が、次のように定義される "__init__()"
   メソッドに含まれます。

      def __init__(self, a: int, b: int = 0):

   デフォルト値を指定しないフィールドを、デフォルト値を指定したフィー
   ルドの後ろに定義すると、 "TypeError" が送出されます。これは、単一の
   クラスであっても、クラス継承の結果でも起きえます。

dataclasses.field(*, default=MISSING, default_factory=MISSING, init=True, repr=True, hash=None, compare=True, metadata=None, kw_only=MISSING, doc=None)

   一般的で単純な用途では、これ以外の機能は必要ありません。 ただし、デ
   ータクラスの機能には、フィールドごとの追加情報を必要とするものもあ
   ります。 追加情報を指定するために、フィールドのデフォルト値を、提供
   されている "field()" 関数の呼び出しに置き換えられます。 例えば次の
   ようにします。

      @dataclass
      class C:
          mylist: list[int] = field(default_factory=list)

      c = C()
      c.mylist += [1, 2, 3]

   上記の "MISSING" 値は、ユーザーが一部のパラメータを指定したかどうか
   を検出するための "sentinel" オブジェクトです。 一部のパラメータでは
   "None" が固有の意味を持つ有効な値なので、このセンチネルを使用してい
   ます。 "MISSING" 値を直接使用するコードを書いてはいけません。

   "field()" のパラメータは以下のとおりです:

   * *default*: 指定した場合、このフィールドのデフォルト値になります。
     通常デフォルト値を指定する位置が "field()" 呼び出しで置き換わるた
     め、このパラメータが必要になります。

   * *default_factory*: 指定する場合、引数を取らない呼び出し可能オブジ
     ェクトでなければなりません。 このフィールドのデフォルト値が必要な
     ときに呼び出されます。 用途の一例として、後述するように、可変なデ
     フォルト値を持つフィールドの指定に使用できます。 *default* と
     *default_factory* を両方指定するとエラーになります。

   * *init*: 真の場合 (デフォルト)、このフィールドを、生成される
     "__init__()" メソッドのパラメータに含めます。

   * *repr*: 真の場合 (デフォルト)、このフィールドを、生成される
     "__repr__()" メソッドが返す文字列に含めます。

   * *hash*: bool または "None" を指定できます。 真の場合、このフィー
     ルドを、生成される "__hash__()" メソッドに含めます。 偽の場合、こ
     のフィールドを、生成される "__hash__()" から除外します。 "None"
     の場合 (デフォルト)、*compare* の値を使用します。 比較に使用する
     フィールドはハッシュにも含めるべきなので、通常はこれが期待される
     動作です。 この値を "None" 以外に設定することは推奨されません。

     フィールドのハッシュ値を計算するコストが高い場合に、 "hash=False"
     だが "compare=True" と設定する理由が 1 つあるとすれば、フィールド
     が等価検査に必要かつ、その型のハッシュ値を計算するのに他のフィー
     ルドも使われることです。 フィールドがハッシュから除外されていたと
     しても、比較には使えます。

   * *compare*: 真の場合 (デフォルト)、このフィールドを、生成される等
     価比較および順序比較のメソッド ("__eq__()"、"__gt__()" など) に含
     めます。

   * *metadata*: マッピングまたは "None" を指定できます。 "None" は空
     の辞書として扱われます。 この値は "MappingProxyType()" でラップし
     て読み取り専用にし、"Field" オブジェクト上で公開します。 データク
     ラス自体はこの値を一切使用せず、サードパーティの拡張機構として提
     供しています。 複数のサードパーティがそれぞれ独自のキーを持ち、メ
     タデータ内の名前空間として使用できます。

   * *kw_only*: 真の場合、このフィールドをキーワード専用に指定します。
     これは、生成される "__init__()" メソッドのパラメータを算出する際
     に使用されます。

     キーワード専用フィールドは "__match_args__" にも含まれません。

      Added in version 3.10.

   * *doc*: このフィールドの省略可能な docstring です。

      Added in version 3.14.

   フィールドのデフォルト値を "field()" の呼び出しで指定した場合、その
   フィールドのクラス属性は、指定した *default* 値で置き換えられます。
   *default* を指定しなかった場合、クラス属性は削除されます。 これは、
   "@dataclass" デコレータの実行後、デフォルト値そのものを指定した場合
   と同じように、クラス属性がすべてフィールドのデフォルト値を持つよう
   にするためです。 例えば、次のコードを実行すると、

      @dataclass
      class C:
          x: int
          y: int = field(repr=False)
          z: int = field(repr=False, default=10)
          t: int = 20

   クラス属性 "C.z" は "10"、クラス属性 "C.t" は "20" になり、クラス属
   性 "C.x" と "C.y" は設定されません。

   バージョン 3.15 で変更: *metadata* が "None" の場合、空の "dict" を
   包んだ "MappingProxyType()" の代わりに、空の "frozendict" を使用す
   るようになりました。

class dataclasses.Field

   "Field" オブジェクトは、定義された各フィールドを記述します。 これら
   のオブジェクトは内部で作成され、モジュールレベルの "fields()" 関数
   (後述) から返されます。 ユーザーが "Field" オブジェクトを直接インス
   タンス化してはいけません。 公開されている属性は以下のとおりです:

   * "name": フィールドの名前。

   * "type": フィールドの型。

   * "default"、"default_factory"、"init"、"repr"、"hash"、"compare"、
     "metadata"、"kw_only" の意味と値は、"field()" 関数のものと同じで
     す。

   他の属性があることもありますが、それらはプライベートであり、調べた
   り、依存したりしてはなりません。

class dataclasses.InitVar

   "InitVar[T]" 型アノテーションは、初期化限定 の変数を記述します。
   "InitVar" でアノテーションされたフィールドは疑似フィールドとみなさ
   れます。 そのため、"fields()" 関数から返されず、"__init__()" および
   存在する場合は "__post_init__()" のパラメータとして追加する以外の用
   途には使われません。

dataclasses.fields(class_or_instance)

   このデータクラスのフィールドを定義する "Field" オブジェクトをタプル
   で返します。 データクラスあるいはデータクラスのインスタンスを受け付
   けます。 データクラスやデータクラスのインスタンスが渡されなかった場
   合は、 "TypeError" を送出します。 "ClassVar" や "InitVar" といった
   疑似フィールドは返しません。

dataclasses.asdict(obj, *, dict_factory=dict)

   データクラス *obj* を (ファクトリ関数 *dict_factory* を使用して) 辞
   書に変換します。 各データクラスは、フィールドを "name: value" のペ
   アとする辞書に変換されます。 データクラス、辞書、frozendict、リスト
   、タプルの内部は再帰的に処理されます。 その他のオブジェクトは
   "copy.deepcopy()" でコピーされます。

   入れ子になったデータクラスに "asdict()" を使用する例です。

      @dataclass
      class Point:
           x: int
           y: int

      @dataclass
      class C:
           mylist: list[Point]

      p = Point(10, 20)
      assert asdict(p) == {'x': 10, 'y': 20}

      c = C([Point(0, 0), Point(10, 4)])
      assert asdict(c) == {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}

   浅いコピーを作成するには、以下の方法が使えます。

      {field.name: getattr(obj, field.name) for field in fields(obj)}

   *obj* がデータクラスのインスタンスでない場合、"asdict()" は
   "TypeError" を送出します。

dataclasses.astuple(obj, *, tuple_factory=tuple)

   データクラス *obj* を (ファクトリ関数 *tuple_factory* を使用して)
   タプルに変換します。 各データクラスは、そのフィールドの値からなるタ
   プルに変換されます。 データクラス、辞書、frozendict、リスト、タプル
   の内部は再帰的に処理されます。 その他のオブジェクトは
   "copy.deepcopy()" でコピーされます。

   1つ前の例の続きです:

      assert astuple(p) == (10, 20)
      assert astuple(c) == ([(0, 0), (10, 4)],)

   浅いコピーを作成するには、以下の方法が使えます。

      tuple(getattr(obj, field.name) for field in dataclasses.fields(obj))

   *obj* がデータクラスのインスタンスでない場合、"astuple()" は
   "TypeError" を送出します。

dataclasses.make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False, module=None, decorator=dataclass)

   名前を *cls_name*、フィールドを *fields* の定義、基底クラスを
   *bases* に指定したものとする新しいデータクラスを作成し、*namespace*
   に指定した名前空間で初期化します。 *fields* は、各要素が "name"、
   "(name, type)"、"(name, type, Field)" のいずれかであるイテラブルで
   す。 "name" だけを指定した場合、"type" には "typing.Any" が使用され
   ます。 *init*、*repr*、*eq*、*order*、*unsafe_hash*、*frozen*、
   *match_args*、*kw_only*、*slots*、*weakref_slot* の値の意味は
   "@dataclass" のものと同じです。

   *module* を指定した場合、データクラスの "__module__" 属性はその値に
   設定されます。 デフォルトでは、呼び出し元のモジュール名に設定されま
   す。

   *decorator* パラメータは、データクラスの作成に使用する呼び出し可能
   オブジェクトです。 最初の引数としてクラスオブジェクトを取り、
   "@dataclass" と同じキーワード引数を受け付ける必要があります。 デフ
   ォルトでは "@dataclass" 関数を使用します。

   "__annotations__" を持つ新しいクラスを作成する Python の機構であれ
   ば、どれでもその後 "@dataclass" 関数を適用してデータクラスに変換で
   きるため、この関数は必須ではありません。 利便性のために提供されてい
   ます。 例えば、次のコードは、

      C = make_dataclass('C',
                         [('x', int),
                           'y',
                          ('z', int, field(default=5))],
                         namespace={'add_one': lambda self: self.x + 1})

   は、次のコードと等しいです:

      @dataclass
      class C:
          x: int
          y: 'typing.Any'
          z: int = 5

          def add_one(self):
              return self.x + 1

   Added in version 3.14: *decorator* パラメータを追加しました。

dataclasses.replace(obj, /, **changes)

   *obj* と同じ型の新しいオブジェクトを作成し、フィールドを *changes*
   の値で置き換えます。 *obj* がデータクラスでない場合は "TypeError"
   を送出します。 *changes* のキーが指定したデータクラスのフィールド名
   でない場合は "TypeError" を送出します。

   新たに返すオブジェクトは、データクラスの "__init__()" メソッドを呼
   び出して作成します。 これにより、"__post_init__()" が存在する場合は
   、それも確実に呼び出されます。

   デフォルト値を持たない初期化限定変数がある場合は、"__init__()" と
   "__post_init__()" に渡せるように、"replace()" の呼び出し時に指定す
   る必要があります。

   *changes* に "init=False" で定義されたフィールドが含まれているとエ
   ラーになります。 この場合は "ValueError" を送出します。

   Be forewarned about how "init=False" fields work during a call to
   "replace()".  They are not copied from the source object, but
   rather are initialized in "__post_init__()", if they're initialized
   at all.  It is expected that "init=False" fields will be rarely and
   judiciously used.  If they are used, it might be wise to have
   alternate class constructors, or perhaps a custom "replace()" (or
   similarly named) method which handles instance copying.

   データクラスのインスタンスは、汎用関数 "copy.replace()" でもサポー
   トされています。

dataclasses.is_dataclass(obj)

   パラメータがデータクラス (データクラスのサブクラスを含みますが、ジ
   ェネリックエイリアス は含みません) またはそのインスタンスであれば
   "True" を、そうでなければ "False" を返します。

   引数がデータクラスのインスタンスである (そして、データクラスそのも
   のではない) かどうかを知る必要がある場合は、 "not isinstance(obj,
   type)" で追加のチェックをしてください:

      def is_dataclass_instance(obj):
          return is_dataclass(obj) and not isinstance(obj, type)

dataclasses.MISSING

   デフォルト値または *default_factory* が指定されていないことを示す
   "sentinel" オブジェクトです。

   バージョン 3.15 で変更: "MISSING" は "sentinel" オブジェクトになり
   ました。

dataclasses.KW_ONLY

   型アノテーションとして使用する "sentinel" オブジェクトです。 型が
   "KW_ONLY" の疑似フィールドより後のフィールドは、すべてキーワード専
   用フィールドとして指定されます。 型が "KW_ONLY" の疑似フィールドは
   、それ以外の点では完全に無視されます。 そのフィールドの名前も無視さ
   れます。 慣例では、"KW_ONLY" フィールドの名前に "_" を使用します。
   キーワード専用フィールドは、クラスのインスタンス化時にキーワードで
   指定する必要がある "__init__()" パラメータを表します。

   このサンプルでは "y" と "z" がキーワード専用フィールドとなります:

      @dataclass
      class Point:
          x: float
          _: KW_ONLY
          y: float
          z: float

      p = Point(0, y=1.5, z=2.0)

   単一のデータクラス内で、型が "KW_ONLY" のフィールドを複数指定すると
   エラーになります。

   Added in version 3.10.

   バージョン 3.15 で変更: "KW_ONLY" は "sentinel" オブジェクトになり
   ました。

exception dataclasses.FrozenInstanceError

   "frozen=True" 付きで定義されたデータクラスで、暗黙的に定義された
   "__setattr__()" または "__delattr__()" が呼び出されたときに送出され
   ます。これは "AttributeError" のサブクラスです。


初期化後の処理
==============

dataclasses.__post_init__()

   クラスに定義されている場合、生成された "__init__()" から、通常は
   "self.__post_init__()" として呼び出されます。 ただし、"InitVar" フ
   ィールドが定義されている場合、それらもクラスで定義された順に
   "__post_init__()" に渡されます。 "__init__()" メソッドが生成されな
   い場合、"__post_init__()" は自動的には呼び出されません。

   用途の一例として、他の一つ以上のフィールドに依存するフィールドの値
   を初期化できます。 例えば次のようにできます。

      @dataclass
      class C:
          a: float
          b: float
          c: float = field(init=False)

          def __post_init__(self):
              self.c = self.a + self.b

"@dataclass" が生成する "__init__()" メソッドは、基底クラスの
"__init__()" メソッドを呼び出しません。 基底クラスに呼び出す必要のある
"__init__()" メソッドがある場合、通常は "__post_init__()" メソッド内で
呼び出します。

   class Rectangle:
       def __init__(self, height, width):
           self.height = height
           self.width = width

   @dataclass
   class Square(Rectangle):
       side: float

       def __post_init__(self):
           super().__init__(self.side, self.side)

ただし、基底クラス自体がデータクラスであれば、派生データクラスがそのす
べてのフィールドを初期化するため、一般にデータクラスが生成した
"__init__()" メソッドを呼び出す必要はありません。

下にある初期化限定変数についての節で、 "__post_init__()" にパラメータ
を渡す方法を参照してください。 "replace()" が "init=False" であるフィ
ールドをどう取り扱うかについての警告も参照してください。


クラス変数
==========

"@dataclass" が実際にフィールドの型を調べる数少ない場面の 1 つは、フィ
ールドが **PEP 526** で定義されるクラス変数かどうかを判定する場合です
。 これは、フィールドの型が "typing.ClassVar" かどうかを調べて行います
。 "ClassVar" であれば、フィールドとしての処理から除外され、データクラ
スの機構では無視されます。 このような "ClassVar" 疑似フィールドは、モ
ジュールレベルの "fields()" 関数から返されません。


初期化限定変数
==============

"@dataclass" が型アノテーションを調べるもう 1 つの場面は、フィールドが
初期化限定変数かどうかを判定する場合です。 これは、フィールドの型が
"InitVar" かどうかを調べて行います。 "InitVar" であれば、初期化限定フ
ィールドという疑似フィールドとみなされます。 本当のフィールドではない
ため、モジュールレベルの "fields()" 関数から返されません。 初期化限定
フィールドは、生成される "__init__()" メソッドのパラメータとして追加さ
れ、"__post_init__()" メソッドがあればそこに渡されます。 データクラス
は、それ以外の用途には使用しません。

例えば、インスタンスの作成時に値が渡されなかった場合に、フィールドをデ
ータベースから初期化するものとします。

   @dataclass
   class C:
       i: int
       j: int | None = None
       database: InitVar[DatabaseType | None] = None

       def __post_init__(self, database):
           if self.j is None and database is not None:
               self.j = database.lookup('j')

   c = C(10, database=my_database)

この場合、"fields()" は "i" と "j" の "Field" オブジェクトを返しますが
、"database" のものは返しません。


凍結されたインスタンス
======================

真に不変な Python オブジェクトを作成することはできません。 ただし、
"@dataclass" デコレータに "frozen=True" を渡すことで、不変性をエミュレ
ートできます。 この場合、データクラスは "__setattr__()" と
"__delattr__()" メソッドをクラスに追加します。 これらのメソッドは呼び
出されると "FrozenInstanceError" を送出します。

"frozen=True" を使用すると、パフォーマンスがわずかに低下します。
"__init__()" がフィールドの初期化に単純な代入を使えず、
"object.__setattr__()" を使用しなければならないためです。


継承
====

"@dataclass" デコレータによるデータクラスの作成時には、クラスのすべて
の基底クラスを MRO の逆順 (つまり "object" から始まる順序) に調べ、見
つかった各データクラスのフィールドを、順序付きのフィールドのマッピング
に追加します。 すべての基底クラスのフィールドを追加した後、自身のフィ
ールドをその順序付きマッピングに追加します。 生成されるすべてのメソッ
ドは、このように計算して統合した順序付きのフィールドのマッピングを使用
します。 フィールドは挿入順に並ぶため、派生クラスが基底クラスをオーバ
ーライドします。 例えば次のようになります。

   @dataclass
   class Base:
       x: Any = 15.0
       y: int = 0

   @dataclass
   class C(Base):
       z: int = 10
       x: int = 15

最終的なフィールドのリストは、順に "x"、"y"、"z" となります。 "x" の最
終的な型は、クラス "C" で指定された "int" です。

"C" に生成される "__init__()" メソッドは、次のようになります。

   def __init__(self, x: int = 15, y: int = 0, z: int = 10):


"__init__()" のキーワード専用パラメータの並べ替え
=================================================

"__init__()" で必要なパラメータが算出されると、キーワード専用引数は他
の一般的な（非キーワード専用）パラメータの後に移動します。これは、すべ
てのキーワード専用引数は、非キーワード専用パラメータの末尾にこなければ
ならないという、キーワード専用パラメータのPythonの実装の都合で必要なこ
とです。

この例では、"Base.y"、"Base.w"、"D.t" はキーワード専用フィールドで、
"Base.x" と "D.z" は通常のフィールドです。

   @dataclass
   class Base:
       x: Any = 15.0
       _: KW_ONLY
       y: int = 0
       w: int = 1

   @dataclass
   class D(Base):
       z: int = 10
       t: int = field(kw_only=True, default=0)

"D" に生成される "__init__()" メソッドは、次のようになります。

   def __init__(self, x: Any = 15.0, z: int = 10, *, y: int = 0, w: int = 1, t: int = 0):

引数の順序はフィールドのリストに現れる順序から変更されています。 通常
のフィールドに対応する引数の後に、キーワード専用フィールドに対応する引
数が続きます。

並べ替え後の "__init__()" のパラメータリストでは、キーワード専用パラメ
ータ同士の相対的な順序は保持されます。


デフォルトファクトリ関数
========================

"field()" に *default_factory* が指定されている場合、フィールドのデフ
ォルト値が必要になると、引数なしで呼び出されます。 例えば、リストの新
しいインスタンスを作成するには、次のようにします。

   mylist: list = field(default_factory=list)

フィールドが ("init=False" によって) "__init__()" から除外されており、
そのフィールドに *default_factory* も指定されている場合、生成された
"__init__()" 関数からデフォルトファクトリ関数が常に呼び出されます。 こ
れは、フィールドに初期値を与える方法が他にないためです。


可変なデフォルト値
==================

Python はメンバ変数のデフォルト値をクラス属性に保持します。 データクラ
スを使っていない、この例を考えてみましょう:

   class C:
       x = []
       def add(self, element):
           self.x.append(element)

   o1 = C()
   o2 = C()
   o1.add(1)
   o2.add(2)
   assert o1.x == [1, 2]
   assert o1.x is o2.x

想定どおり、クラス "C" の 2 つのインスタンスが同じクラス変数 "x" を共
有していることに注意してください。

データクラスを使っているこのコードが *もし仮に* 有効なものだとしたら:

   @dataclass
   class D:
       x: list = []      # このコードは ValueError を送出する
       def add(self, element):
           self.x.append(element)

データクラスは次のようなコードを生成するでしょう:

   class D:
       x = []
       def __init__(self, x=x):
           self.x = x
       def add(self, element):
           self.x.append(element)

   assert D().x is D().x

これには、クラス "C" を使用した最初の例と同じ問題があります。 つまり、
クラスインスタンスを作成する際に "x" の値を指定しなかったクラス "D" の
2 つのインスタンスは、同じ "x" を共有します。 データクラスも通常の
Python のクラス作成を使用するだけなので、この動作を共有します。 データ
クラスがこの状況を検出する一般的な方法はありません。 代わりに、
"@dataclass" デコレータは、ハッシュ不可能なデフォルトのパラメータを検
出すると "ValueError" を送出します。 これは、ハッシュ不可能な値は可変
であるという仮定に基づきます。 部分的な解決策ですが、よくある多くの誤
りを防げます。

デフォルトファクトリ関数を使うのが、フィールドのデフォルト値として可変
な型の新しいインスタンスを作成する手段です:

   @dataclass
   class D:
       x: list = field(default_factory=list)

   assert D().x is not D().x

バージョン 3.11 で変更: "list"、"dict"、"set" 型のオブジェクトを検出し
て禁止する代わりに、ハッシュ不可能なオブジェクトをデフォルト値として禁
止するようになりました。 ハッシュ不可能かどうかを、可変かどうかの近似
的な判定に使用しています。


デスクリプタ型のフィールド
==========================

デフォルト値として デスクリプタオブジェクト が代入されたフィールドは、
以下のような特別な動作をします:

* データクラスの "__init__()" メソッドに渡されたフィールドの値は、デス
  クリプタオブジェクトを上書きするのではなく、デスクリプタの
  "__set__()" メソッドに渡されます。

* 同様に、フィールドを取得または設定すると、デスクリプタオブジェクトを
  返したり上書きしたりする代わりに、デスクリプタの "__get__()" または
  "__set__()" メソッドを呼び出します。

* フィールドがデフォルト値を持つかどうかを判定するため、"@dataclass"
  はデスクリプタの "__get__()" メソッドを、クラスからアクセスする形式
  "descriptor.__get__(obj=None, type=cls)" で呼び出します。 この場合に
  デスクリプタが値を返せば、その値をフィールドのデフォルト値として使用
  します。 一方、この状況でデスクリプタが "AttributeError" を送出した
  場合、フィールドにはデフォルト値が設定されません。

   class IntConversionDescriptor:
       def __init__(self, *, default):
           self._default = default

       def __set_name__(self, owner, name):
           self._name = "_" + name

       def __get__(self, obj, type):
           if obj is None:
               return self._default

           return getattr(obj, self._name, self._default)

       def __set__(self, obj, value):
           setattr(obj, self._name, int(value))

   @dataclass
   class InventoryItem:
       quantity_on_hand: IntConversionDescriptor = IntConversionDescriptor(default=100)

   i = InventoryItem()
   print(i.quantity_on_hand)   # 100
   i.quantity_on_hand = 2.5    # 2.5 を渡して __set__ を呼び出す
   print(i.quantity_on_hand)   # 2

フィールドにデスクリプタ型のアノテーションが付いていても、デフォルト値
としてデスクリプタオブジェクトが代入されていなければ、通常のフィールド
と同じように動作することに注意してください。
