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

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

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

このモジュールは、"__init__()" や "__repr__()" のような *special
method* を生成し、ユーザー定義のクラスに自動的に追加するデコレータや関
数を提供します。このモジュールは **PEP 557** に記載されました。

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

   from dataclasses import dataclass

   @dataclass
   class InventoryItem:
       """Class for keeping track of an item in inventory."""
       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" クラスの定義中にこのメソッドが直接明記されるわけではあ
りません。

バージョン 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)

   この関数は、後述する *special method* を生成し、クラスに追加する
   *decorator* です。

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

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

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

   "dataclass()" が引数を指定しない単純なデコレータとして使用された場
   合、ドキュメントに記載されているシグネチャのデフォルト値のとおりに
   動作します。つまり、以下の３つの "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)
      class C:
         ...

   "dataclass()" の引数は以下の通りです:

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

     もしクラスに "__init__()" が既に定義されていた場合は、この引数は
     無視されます。

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

     もしクラスに "__repr__()" が既に定義されていた場合は、この引数は
     無視されます。

   * "eq": (デフォルトの)真の場合、 "__eq__()" メソッドが生成されます
     。このメソッドはクラスの比較を、そのクラスのフィールドからなるタ
     プルを比較するように行います。比較する2つのインスタンスのクラスは
     同一でなければなりません。

     もしクラスに "__eq__()" が既に定義されていた場合は、この引数は無
     視されます。

   * "order": 真 (デフォルト値は "False") の場合、 "__lt__()"、
     "__le__()"、"__gt__()"、"__ge__()" メソッドが生成されます。これら
     の比較は、クラスをそのフィールドからなるタプルであるかのように取
     り扱います。比較される2つのインスタンスは、同一の型でなければなり
     ません。もし "order" が true で、 "eq" に falseを指定すすると、
     "ValueError" が送出されます。

     もし、クラスで既に "__lt__()", "__le__()", "__gt__()", "__ge__()"
     のうちいずれかが定義されていると "TypeError" が送出されます。

   * "unsafe_hash": (デフォルトの) "False" の場合、 "eq" と "frozen"
     がどう設定されているかに従って "__hash__()" メソッドが生成されま
     す。

     "__hash__()" は、組み込みの "hash()" から使われたり、 dict や set
     のようなハッシュ化されたコレクションにオブジェクトを追加するとき
     に使われます。 "__hash__()" があるということはそのクラスのインス
     タンスが不変 (イミュータブル) であることを意味します。 可変性とい
     うのは複雑な性質で、プログラマの意図、 "__eq__()" が存在している
     かどうかとその振る舞い、 "dataclass()" デコレータの "eq" フラグと
     "frozen" フラグの値に依存します。

     デフォルトでは、 "dataclass()" は追加しても安全でない限り
     "__hash__()" メソッドを暗黙的には追加しません。 また、明示的に定
     義され存在している "__hash__()" メソッドに追加したり変更したりは
     しません。 クラスの属性の "__hash__ = None" という設定は、 Python
     にとって "__hash__()" のドキュメントにあるような特別な意味があり
     ます。

     "__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__()" がクラ
     スに定義されていた場合は、 "TypeError" が送出されます。 後にある
     議論を参照してください。

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

      バージョン 3.10 で追加.

   * "kw_only": もし真（デフォルトは "False" ）なら、すべてのフィール
     ドはキーワード専用となります。もしフィールドにキーワード専用の指
     定がされると、 "__init__()" の引数はキーワード専用フィールドから
     作られ、 "__init__()" 呼び出し時にキーワードを指定しなければなら
     なくなります。このオプションはデータクラスの他の機能には影響はあ
     りません。詳細は用語集の *parameter* を参照してください。もしくは
     "KW_ONLY" セクションを参照してください。

      バージョン 3.10 で追加.

   * "slots": もし真なら（デフォルトは "False" ）、 "__slots__" 属性が
     作られ、オリジナルのクラスの代わりに新しいクラスが返されます。も
     し "__slots__" がすでにクラスに定義されていた場合、 "TypeError"
     が送出されます。

      バージョン 3.10 で追加.

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

      @dataclass
      class C:
          a: int       # 'a' has no default value
          b: int = 0   # assign a default value for 'b'

   この例では、生成された "__init__()" メソッドには "a" と "b" の両方
   が含まれ、以下のように定義されます:

      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)

   通常の単純なユースケースでは、この他の機能は必要ありません。 しかし
   、データクラスには、フィールドごとの情報を必要とする機能もあります
   。 追加の情報の必要性に応えるために、デフォルトのフィールドの値をモ
   ジュールから提供されている "field()" 関数の呼び出しに置き換えられま
   す。 例えば次のようになります:

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

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

   As shown above, the "MISSING" value is a sentinel object used to
   detect if some parameters are provided by the user. This sentinel
   is used because "None" is a valid value for some parameters with a
   distinct meaning.  No code should directly use the "MISSING" value.

   "field()" の引数は次の通りです:

   * "default": 与えられた場合、このフィールドのデフォルト値になります
     。 これが必要なのは、 "field()" の呼び出しそのものが通常ではデフ
     ォルト値がいる位置を横取りしているからです。

   * "default_factory": 提供されていた場合、0 引数の呼び出し可能オブジ
     ェクトでなければならず、このフィールドの初期値が必要になったとき
     に呼び出されます。 他の目的も含めて、下で議論されているように、フ
     ィールドに可変なデフォルト値を指定するのに使えます。 "default" と
     "default_factory" の両方を指定するとエラーになります。

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

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

   * "hash": これは真偽値あるいは "None" に設定できます。 真の場合、こ
     のフィールドは、生成された "__hash__()" メソッドに含まれます。 (
     デフォルトの) "None" の場合、 "compare" の値を使います: こうする
     ことは普通は期待通りの振る舞いになります。 比較で使われるフィール
     ドはハッシュに含まれるものと考えるべきです。 この値を "None" 以外
     に設定することは推奨されません。

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

   * "compare": (デフォルトの) 真の場合、生成される等価関数と比較関数(
     "__eq__()" 、"__gt__()" など)にこのフィールドを含めます。

   * "metadata": これはマッピングあるいは "None" に設定できます。
     "None" は空の辞書として扱われます。 この値は "MappingProxyType()"
     でラップされ、読み出し専用になり、 "Field" オブジェクトに公開され
     ます。 これはデータクラスから使われることはなく、サードパーティー
     の拡張機構として提供されます。 複数のサードパーティーが各々のキー
     を持て、メタデータの名前空間として使えます。

   * "kw_only": もし真なら、このフィールドはキーワード専用となります。
     これは生成された "__init__()" メソッドがパラメータを評価する時に
     利用されます。

      バージョン 3.10 で追加.

   "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" には値が設定されません。

class dataclasses.Field

   "Field" オブジェクトはそれぞれの定義されたフィールドを記述します。
   このオブジェクトは内部で作られ、モジュールレベル関数の "fields()"
   によって返されます (下の解説を見てください)。 ユーザーは絶対に
   "Field" オブジェクトを直接インスタンス化すべきではありません。 ドキ
   ュメント化されている属性は次の通りです:

      * "name": フィールド名

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

      * "default", "default_factory", "init", "repr", "hash",
        "compare", "metadata", "kw_only" は "field()" の宣言と同じ意味
        と値を持ちます。

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

dataclasses.fields(class_or_instance)

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

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

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

   Example of using "asdict()" on nested dataclasses:

      @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}]}

   To create a shallow copy, the following workaround may be used:

      dict((field.name, getattr(obj, field.name)) for field in fields(obj))

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

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

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

   1つ前の例の続きです:

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

   To create a shallow copy, the following workaround may be used:

      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)

   "cls_name" という名前、 "fields" で定義されるフィールド、 "bases"
   で与えられた基底クラス、 "namespace" で与えられた名前空間付きで初期
   化されたデータクラスを作成します。 "fields" はイテラブルで、要素が
   "name", "(name, type)", "(name, type, Field)" のうちのどれかです。
   単に "name" だけが与えられた場合は、 "typing.Any" が "type" として
   使われます。 "init", "repr", "eq", "order", "unsafe_hash",
   "frozen", "match_args", "kw_only" の値は、 "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

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

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

   新しく返されるオブジェクトは、データクラスの "__init__()" メソッド
   を呼び出して作成されます。 これにより、もしあれば "__post_init__()"
   も呼び出されることが保証されます。

   初期化限定変数でデフォルト値を持たないものがもしあれば、
   "replace()" の呼び出し時に初期値が指定され、 "__init__()" と
   "__post_init__()" に渡せるようにしなければなりません。

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

   "replace()" を呼び出しているときに "init=False" であるフィールドが
   どのように働くかに気を付けてください。 そのフィールドは元のオブジェ
   クトからコピーされるのではなく、仮に初期化されたとしても結局は
   "__post_init__()" で初期化されます。 "init=False" であるフィールド
   は滅多に使いませんし、使うとしたら注意深く使用します。 そのようなフ
   ィールドが使われている場合は、代わりのクラスコンストラクタ、あるい
   は、インスタンスのコピー処理をする独自実装の "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が設定されてない場合の番兵の値を設定し
   ます。

dataclasses.KW_ONLY

   番兵値は型アノテーションとして使用されます。 "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)

   1つのデータクラスの中に、2つ以上の "KW_ONLY" の型のフィールドがある
   とエラーになります。

   バージョン 3.10 で追加.

exception dataclasses.FrozenInstanceError

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


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

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

他の機能と組み合わせることで、他の 1 つ以上のフィールドに依存している
フィールドが初期化できます。 例えば次のようにできます:

   @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__()"  メソッドを呼びません。もし、ベースクラスが :meth:
*__init__* メソッドを持ち、それを呼ぶ必要があれば、 "__post_init__()"
メソッドの中から呼び出します:

   @dataclass
   class Rectangle:
       height: float
       width: float

   @dataclass
   class Square(Rectangle):
       side: float

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

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

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


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

One of the few places where "dataclass()" actually inspects the type
of a field is to determine if a field is a class variable as defined
in **PEP 526**.  It does this by checking if the type of the field is
"typing.ClassVar".  If a field is a "ClassVar", it is excluded from
consideration as a field and is ignored by the dataclass mechanisms.
Such "ClassVar" pseudo-fields are not returned by the module-level
"fields()" function.


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

Another place where "dataclass()" inspects a type annotation is to
determine if a field is an init-only variable.  It does this by seeing
if the type of a field is of type "dataclasses.InitVar".  If a field
is an "InitVar", it is considered a pseudo-field called an init-only
field.  As it is not a true field, it is not returned by the module-
level "fields()" function.  Init-only fields are added as parameters
to the generated "__init__()" method, and are passed to the optional
"__post_init__()" method.  They are not otherwise used by dataclasses.

例えば、あるフィールドがデータベースから初期化されると仮定して、クラス
を作成するときには値が与えられない次の場合を考えます:

   @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" の "Field" オブジェクトは返しません。


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

真に不変な Python のオブジェクトを作成するのは不可能です。 しかし、
"frozen=True" を "dataclass()" デコレータに渡すことで、不変性の模倣は
できます。 このケースでは、データクラスは "__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 = []
          def add(self, element):
              self.x += element

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

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

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

   これには、クラス "C" を使ったさっきの例と同じ問題があります。 すな
   わち、クラス "D" の 2 つのインスタンスは、クラスインスタンスを作成
   するときに "x" の具体的な値を指定しておらず、同じ "x" のコピーを共
   有します。 データクラスは Python の通常のクラス作成の仕組みを使って
   いるだけなので、この同じ問題を抱えています。 データクラスがこの問題
   を検出する一般的な方法を持たない代わりに、 "dataclass()" デコレータ
   は型が "list" や "dict" や "set" のデフォルトパラメーターを検出した
   場合、 "TypeError" を送出します。 これは完全ではない解決法ですが、
   よくあるエラーの多くを防げます。

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

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

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


Descriptor-typed fields
=======================

Fields that are assigned descriptor objects as their default value
have the following special behaviors:

* The value for the field passed to the dataclass's "__init__" method
  is passed to the descriptor's "__set__" method rather than
  overwriting the descriptor object.

* Similarly, when getting or setting the field, the descriptor's
  "__get__" or "__set__" method is called rather than returning or
  overwriting the descriptor object.

* To determine whether a field contains a default value, "dataclasses"
  will call the descriptor's "__get__" method using its class access
  form (i.e. "descriptor.__get__(obj=None, type=cls)".  If the
  descriptor returns a value in this case, it will be used as the
  field's default. On the other hand, if the descriptor raises
  "AttributeError" in this situation, no default value will be
  provided for the field.

   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    # calls __set__ with 2.5
   print(i.quantity_on_hand)   # 2

Note that if a field is annotated with a descriptor type, but is not
assigned a descriptor object as its default value, the field will act
like a normal field.
