# `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.

## Module contents

@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)

  이 함수는 (아래에서 설명하는) 생성된 특수 메서드를 클래스에 추가하는데 사용되는 데코레이터 입니다.

  `@dataclass` 데코레이터는 클래스를 검사하여 필드를 찾습니다. 필드는 형 어노테이션을 가진 클래스 변수로 정의됩니다. 아래에 설명된 두 가지 예외를 제외하고는, `@dataclass` 는 변수 어노테이션에 지정된 형을 검사하지 않습니다.

  생성된 모든 메서드의 필드 순서는 클래스 정의에 나타나는 순서입니다.

  `@dataclass` 데코레이터는 여러 “던더(dunder)” 메서드들을 클래스에 추가하는데, 아래에서 설명합니다. 추가할 메서드가 클래스에 이미 존재하면, 동작은 아래에 설명된 대로 매개변수에 따라 다릅니다. 데코레이터는 호출된 클래스와 같은 클래스를 반환합니다; 새 클래스가 만들어지지 않습니다.

  `@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, 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__()` 를 정의했으면, 이 매개변수는 무시됩니다.

  - order: 참이면 (기본값은 `False`), `__lt__()`, `__le__()`, `__gt__()`, `__ge__()` 메서드가 생성됩니다. 이것들은 클래스를 필드의 튜플인 것처럼 순서대로 비교합니다. 비교되는 두 인스턴스는 같은 형이어야 합니다. order 가 참이고 eq 가 거짓이면 `ValueError` 가 발생합니다.

    클래스가 이미 `__lt__()`, `__le__()`, `__gt__()`, `__ge__()` 중 하나를 정의하고 있다면 `TypeError` 가 발생합니다.

  - unsafe_hash: If true, force `dataclasses` to create a `__hash__()` method, even though it may not be safe to do so. Otherwise, generate a `__hash__()` method according to how eq and frozen are set. The default value is `False`.

    `__hash__()` 는 내장 `hash()` 에 의해 사용되며, 딕셔너리와 집합 같은 해시 컬렉션에 객체가 추가될 때 사용됩니다. `__hash__()` 를 갖는다는 것은 클래스의 인스턴스가 불변이라는 것을 의미합니다. 가변성은 프로그래머의 의도, `__eq__()` 의 존재와 행동, `@dataclass` 데코레이터의 eq 와 frozen 플래그의 값에 의존하는 복잡한 성질입니다.

    기본적으로, `@dataclass` 는 안전하지 않다면 `__hash__()` 메서드를 묵시적으로 추가하지 않습니다. 기존에 명시적으로 정의된 `__hash__()` 메서드를 추가하거나 변경하지도 않습니다. `__hash__()` 문서에서 설명된 대로, 클래스 어트리뷰트를 `__hash__ = None` 로 설정하는 것은 파이썬에 특별한 의미가 있습니다.

    `__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: If true (the default is `False`), assigning to fields will generate an exception. This emulates read-only frozen instances. See the discussion below.

    If `__setattr__()` or `__delattr__()` is defined in the class and frozen is true, then `TypeError` is raised.

  - match_args: If true (the default is `True`), the `__match_args__` tuple will be created from the list of non keyword-only parameters to the generated `__init__()` method (even if `__init__()` is not generated, see above). If false, or if `__match_args__` is already defined in the class, then `__match_args__` will not be generated.

  > Added in version 3.10.

  - kw_only: If true (the default value is `False`), then all fields will be marked as keyword-only. If a field is marked as keyword-only, then the only effect is that the `__init__()` parameter generated from a keyword-only field must be specified with a keyword when `__init__()` is called. See the parameter glossary entry for details. Also see the `KW_ONLY` section.

    Keyword-only fields are not included in `__match_args__`.

  > Added in version 3.10.

  - slots: 참이면 (기본값은 `False`), `__slots__` 어트리뷰트가 만들어지고 원래 클래스 대신 새 클래스가 반환됩니다. `__slots__` 이 클래스에 이미 정의되어 있다면 `TypeError` 가 발생합니다.

  > 경고

  > Calling no-arg `super()` in dataclasses using `slots=True` will result in the following exception being raised: `TypeError: super(type, obj): obj must be an instance or subtype of type`. The two-arg `super()` is a valid workaround. See gh-90562 for full details.

  > 경고

  > Passing parameters to a base class `__init_subclass__()` when using `slots=True` will result in a `TypeError`. Either use `__init_subclass__` with no parameters or use default values as a workaround. See gh-91126 for full details.

  > Added in version 3.10.

  > 버전 3.11에서 변경: If a field name is already included in the `__slots__` of a base class, it will not be included in the generated `__slots__` to prevent overriding them. Therefore, do not use `__slots__` to retrieve the field names of a dataclass. Use `fields()` instead. To be able to determine inherited slots, base class `__slots__` may be any iterable, but not an iterator.

  - weakref_slot: If true (the default is `False`), add a slot named “__weakref__”, which is required to make an instance `weakref-able`. It is an error to specify `weakref_slot=True` without also specifying `slots=True`.

  > Added in version 3.11.

  필드는 선택적으로 일반적인 파이썬 문법을 사용하여 기본값을 지정할 수 있습니다:

      @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)

  일반적이고 간단한 사용 사례의 경우 다른 기능은 필요하지 않습니다. 그러나 필드별로 추가 정보가 필요한 일부 데이터 클래스 기능이 있습니다. 추가 정보에 대한 필요성을 충족시키기 위해, 기본 필드 값을 제공된 `field()` 함수 호출로 바꿀 수 있습니다. 예를 들면:

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

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

  위에서 보인 것처럼, `MISSING` 값은 사용자가 일부 매개변수를 제공했는지를 탐지하는데 사용되는 표지 객체입니다. 일부 매개변수에 대해서는 `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` 로 설정하는 한 가지 가능한 이유는, 동등 비교에 포함되는 필드가 해시값을 계산하는 데 비용이 많이 들고, 형의 해시값에 이바지하는 다른 필드가 있는 경우입니다. 필드가 해시에서 제외된 경우에도 비교에는 계속 사용됩니다.

  - compare: 참(기본값)이면, 이 필드는 생성된 같음 및 비교 메서드(`__eq__()`, `__gt__()` 등)에 포함됩니다.

  - metadata: 매핑이나 `None`이 될 수 있습니다. `None`은 빈 딕셔너리로 취급됩니다. 이 값은 `MappingProxyType()` 로 감싸져서 읽기 전용으로 만들어지고, `Field` 객체에 노출됩니다. 데이터 클래스에서는 전혀 사용되지 않으며, 제삼자 확장 메커니즘으로 제공됩니다. 여러 제삼자는 이름 공간으로 사용할 자신만의 키를 가질 수 있습니다.

  - kw_only: If true, this field will be marked as keyword-only. This is used when the generated `__init__()` method’s parameters are computed.

    Keyword-only fields are also not included in `__match_args__`.

  > Added in version 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()` 함수에서와 같은 의미와 값을 가지고 있습니다.

  다른 어트리뷰트도 있을 수 있지만, 내부적인 것이므로 검사하거나 의존해서는 안 됩니다.

class dataclasses.InitVar

  `InitVar[T]` type annotations describe variables that are init-only. Fields annotated with `InitVar` are considered pseudo-fields, and thus are neither returned by the `fields()` function nor used in any way except adding them as parameters to `__init__()` and an optional `__post_init__()`.

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:

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

  `asdict()`는 obj 가 데이터 클래스 인스턴스가 아닌 경우 `TypeError` 를 일으킵니다.

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

  데이터 클래스 obj 를 튜플로 변환합니다 (팩토리 함수 tuple_factory 를 사용합니다). 각 데이터 클래스는 각 필드 값들의 튜플로 변환됩니다. 데이터 클래스, 딕셔너리, 리스트 및 튜플은 재귀적으로 변환됩니다. 다른 객체들은 `copy.deepcopy()`로 복사됩니다.

  이전 예에서 계속하면:

      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))

  `astuple()`은 obj 가 데이터 클래스 인스턴스가 아닌 경우 `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)

  새로운 데이터 클래스를 만드는데, 이름은 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, slots 및 weakref_slot 의 값은 `@dataclass` 에서와 같은 의미가 있습니다.

  If module is defined, the `__module__` attribute of the dataclass is set to that value. By default, it is set to the module name of the caller.

  이 함수가 꼭 필요하지는 않습니다. 임의의 파이썬 메커니즘으로 `__annotations__` 을 갖는 새 클래스를 만든 후에 `@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()` (또는 비슷하게 이름 지어진) 메서드를 사용하는 것이 좋을 것입니다.

  Dataclass instances are also supported by generic function `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

  A sentinel value signifying a missing default or default_factory.

dataclasses.KW_ONLY

  A sentinel value used as a type annotation. Any fields after a pseudo-field with the type of `KW_ONLY` are marked as keyword-only fields. Note that a pseudo-field of type `KW_ONLY` is otherwise completely ignored. This includes the name of such a field. By convention, a name of `_` is used for a `KW_ONLY` field. Keyword-only fields signify `__init__()` parameters that must be specified as keywords when the class is instantiated.

  In this example, the fields `y` and `z` will be marked as keyword-only fields:

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

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

  In a single dataclass, it is an error to specify more than one field whose type is `KW_ONLY`.

  Added in version 3.10.

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

The `__init__()` method generated by `@dataclass` does not call base class `__init__()` methods. If the base class has an `__init__()` method that has to be called, it is common to call this method in a `__post_init__()` method:

    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)

Note, however, that in general the dataclass-generated `__init__()` methods don’t need to be called, since the derived dataclass will take care of initializing all fields of any base class that is a dataclass itself.

매개변수를 `__post_init__()` 에 전달하는 방법은 초기화 전용 변수에 대한 아래 섹션을 참조하십시오. 또한 `replace()` 가 `init=False` 필드를 처리하는 방식에 관한 경고를 보십시오.

## 클래스 변수

`@dataclass` 가 실제로 필드의 형을 검사하는 몇 안 되는 곳 중 하나는 필드가 PEP 526 에 정의된 클래스 변수인지를 확인하는 것입니다. 필드의 형이 `typing.ClassVar` 인지 검사합니다. 필드가 `ClassVar` 인 경우, 필드로 취급되지 않고 데이터 클래스 메커니즘에서 무시됩니다. 이런 `ClassVar` 의사 필드는 모듈 수준 `fields()` 함수에 의해 반환되지 않습니다.

## 초기화 전용 변수

`@dataclass` 가 형 어노테이션을 검사하는 다른 곳은 필드가 초기화 전용 변수인지 확인하는 것입니다. 필드의 형이 `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` 는 반환하지 않습니다.

## 고정 인스턴스

정말로 불변인 파이썬 객체를 만드는 것은 불가능합니다. 그러나, `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):

## Re-ordering of keyword-only parameters in `__init__()`

After the parameters needed for `__init__()` are computed, any keyword-only parameters are moved to come after all regular (non-keyword-only) parameters. This is a requirement of how keyword-only parameters are implemented in Python: they must come after non-keyword-only parameters.

In this example, `Base.y`, `Base.w`, and `D.t` are keyword-only fields, and `Base.x` and `D.z` are regular fields:

    @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):

Note that the parameters have been re-ordered from how they appear in the list of fields: parameters derived from regular fields are followed by parameters derived from keyword-only fields.

The relative ordering of keyword-only parameters is maintained in the re-ordered `__init__()` parameter list.

## 기본 팩토리 함수

`field()` 가 default_factory 를 지정하면, 필드의 기본값이 필요할 때 인자 없이 호출됩니다. 예를 들어, 리스트의 새 인스턴스를 만들려면, 이렇게 하세요:

    mylist: list = field(default_factory=list)

필드가 (`init=False` 를 사용해서) `__init__()` 에서 제외되고, 그 필드가 default_factory 를 지정하면, 생성된 `__init__()` 함수는 항상 기본 팩토리 함수를 호출합니다. 이는 필드에 초기화 값을 제공할 수 있는 다른 방법이 없기 때문입니다.

## 가변 기본값

파이썬은 기본 멤버 변숫값을 클래스 어트리뷰트에 저장합니다. 데이터 클래스를 사용하지 않는 이 예제를 생각해보세요:

    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` 의 두 인스턴스는 예상대로 같은 클래스 변수 `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` 의 두 인스턴스는 `x`의 같은 사본을 공유합니다. 데이터 클래스는 일반적인 파이썬 클래스 생성을 사용하기 때문에, 이 동작 역시 공유합니다. 데이터 클래스가 이 조건을 감지하는 일반적인 방법은 없습니다. 대신, `@dataclass` 데코레이터는 해시 불가능한 기본 매개변수를 탐지하면 `ValueError` 를 발생시킵니다. 값이 해시 불가능하면, 가변이라는 가정입니다. 이것은 부분적인 해결책이지만, 흔한 오류로부터 보호합니다.

기본 팩토리 함수를 사용하면 필드의 기본값으로 가변형의 새 인스턴스를 만들 수 있습니다:

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

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

버전 3.11에서 변경: Instead of looking for and disallowing objects of type `list`, `dict`, or `set`, unhashable objects are now not allowed as default values. Unhashability is used to approximate mutability.

## 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, `@dataclass` will call the descriptor’s `__get__()` method using its class access form: `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    # __set__ 을 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.