데이터 클래스 란 무엇이며 공통 클래스와 어떻게 다릅니 까?


141

PEP 557을 사용하면 데이터 클래스가 파이썬 표준 라이브러리에 도입됩니다.

그들은 @dataclass데코레이터를 사용하고 "기본적으로 변경 가능한 명명 된 튜플"이어야하지만 실제로 이것이 무엇을 의미하고 공통 클래스와 어떻게 다른지 이해하지 못합니다.

파이썬 데이터 클래스는 정확히 무엇이며 언제 사용하는 것이 가장 좋습니까?


8
PEP의 광범위한 내용을 고려할 때 다른 무엇을 알고 싶습니까? namedtuples는 변경할 수 없으며 속성의 기본값을 가질 수 없지만 데이터 클래스는 변경할 수 있으며 가질 수 있습니다.
jonrsharpe

29
@ jonrsharpe 주제에 stackoverflow 스레드가 있어야한다는 것이 합리적입니다. Stackoverflow는 Q & A 형식의 백과 사전입니다. 답은 결코 "이 다른 웹 사이트를 보지"마십시오. 여기에는 공표가 없어야합니다.
Luke Davis

10
항목을 목록에 추가하는 방법에는 5 가지 스레드가 있습니다. 한 가지 질문 @dataclass만으로도 사이트가 붕괴되지는 않습니다.
eric

2
@jonrsharpe namedtuplesCAN에는 기본값이 있습니다. 여기를보세요 : stackoverflow.com/questions/11351032/…
MJB

답변:


152

데이터 클래스는 많은 논리를 포함하는 것보다 상태를 저장하는 데 적합한 일반 클래스입니다. 대부분 속성으로 구성된 클래스를 만들 때마다 데이터 클래스를 만들었습니다.

어떤 dataclasses모듈이하는 것은 만들 것입니다 쉽게 데이터 클래스를 만들 수 있습니다. 그것은 당신을 위해 많은 보일러 플레이트를 관리합니다.

이것은 데이터 클래스가 해시 가능해야 할 때 특히 중요합니다. 여기에는 __hash__방법뿐만 아니라 방법 이 필요합니다 __eq__. __repr__쉽게 디버깅 할 수 있도록 사용자 지정 방법을 추가하면 상당히 장황해질 수 있습니다.

class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

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

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

    def __repr__(self) -> str:
        return (
            'InventoryItem('
            f'name={self.name!r}, unit_price={self.unit_price!r}, '
            f'quantity_on_hand={self.quantity_on_hand!r})'

    def __hash__(self) -> int:
        return hash((self.name, self.unit_price, self.quantity_on_hand))

    def __eq__(self, other) -> bool:
        if not isinstance(other, InventoryItem):
            return NotImplemented
        return (
            (self.name, self.unit_price, self.quantity_on_hand) == 
            (other.name, other.unit_price, other.quantity_on_hand))

이를 통해 dataclasses다음 과 같이 줄일 수 있습니다.

from dataclasses import dataclass

@dataclass(unsafe_hash=True)
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

동일한 클래스의 장식은 비교 방법 (발생 수 __lt__, __gt__등) 및 핸들 불변성.

namedtuple클래스는 데이터 클래스이지만 기본적으로 (및 시퀀스) 변경 불가능합니다. dataclasses이와 관련하여 훨씬 더 유연 하며 namedtuple클래스 와 동일한 역할을 수행 할 수 있도록 쉽게 구성 할 수 있습니다 .

PEP는 슬롯, 유효성 검사기, 변환기, 메타 데이터 등을 포함하여 더 많은 작업을 수행 할 수 있는 attrs프로젝트 에서 영감을 받았습니다 .

몇 가지 예를 보려면 최근에 Advent of Code 솔루션 중 일부에 사용 dataclasses했습니다. 7 일 , 8 일 , 11 일20 의 솔루션을 참조하십시오 .

dataclassesPython 버전 <3.7에서 모듈 을 사용 하려면 백 포트 모듈 (3.6 필요)을 설치하거나 attrs위에서 언급 한 프로젝트를 사용할 수 있습니다 .


2
첫 번째 예에서 같은 이름의 인스턴스 멤버가있는 클래스 멤버를 의도적으로 숨기고 있습니까? 이 관용구를 이해하도록 도와주세요.
VladimirLenin이

4
@VladimirLenin : 클래스 속성이 없으며 형식 주석 만 있습니다. 참조 PEP (526) , 특히 클래스와 인스턴스 변수의 주석 부분을 .
Martijn Pieters

1
@Bananach : 키워드 인수를 기본값으로 사용하여 @dataclass거의 동일한 __init__메소드를 생성 quantity_on_hand합니다. 인스턴스를 만들면 quantity_on_hand항상 인스턴스 속성 이 설정 됩니다. 따라서 데이터 클래스가 아닌 첫 번째 예제는 동일한 패턴을 사용하여 데이터 클래스 생성 코드가 수행 할 작업을 에코합니다.
Martijn Pieters

1
@Bananach : 그래서 첫 번째 예, 우리는 할 수 단지 생략 인스턴스 속성을 설정하고 클래스 속성을 그림자하지, 그 의미에서 어쨌든 중복 설정이 있지만 dataclasses는 않습니다 설정합니다.
Martijn Pieters

1
@ user2853437 사용 사례는 실제로 데이터 클래스에서 지원되지 않습니다. 아마도 데이터 클래스의 더 큰 사촌 attrs을 사용하는 것이 좋습니다 . 이 프로젝트는 필드 값을 정규화 할 수있는 필드 별 변환기 를 지원 합니다. 데이터 클래스를 고수하려면 예에서 __post_init__메소드 에서 정규화를 수행하십시오 .
Martijn Pieters

62

개요

질문이 해결되었습니다. 그러나이 답변은 데이터 클래스에 대한 기본적인 이해를 돕기 위해 실용적인 예제를 추가합니다.

파이썬 데이터 클래스는 정확히 무엇이며 언제 사용하는 것이 가장 좋습니까?

  1. 코드 생성기 : 상용구 코드를 생성합니다. 일반 클래스에서 특수 메소드를 구현하거나 데이터 클래스가 자동으로 구현하도록 선택할 수 있습니다.
  2. 데이터 컨테이너 : 데이터 를 보유하는 구조 (예 : 튜플 및 딕트), 종종 점 등의 속성 액세스 (예 : 클래스 namedtuple등)가있는 구조 .

"default [s]를 갖는 변경 가능한 명명 된 튜플"

후자의 문구가 의미하는 바는 다음과 같습니다.

  • 변경 가능 : 기본적으로 데이터 클래스 속성을 재 할당 할 수 있습니다. 선택적으로 불변으로 만들 수 있습니다 (아래 예 참조).
  • namedtuple : namedtuple일반 클래스 와 같이 점으로 구분 된 속성 액세스가 있습니다 .
  • 기본값 : 속성에 기본값을 할당 할 수 있습니다.

일반적인 클래스와 비교하여 기본적으로 상용구 코드를 입력하면 비용이 절감됩니다.


풍모

이것은 데이터 클래스 기능에 대한 개요입니다 (TL; DR? 다음 섹션의 요약 표 참조).

당신이 얻는 것

다음은 기본적으로 데이터 클래스에서 얻는 기능입니다.

속성 + 표현 + 비교

import dataclasses


@dataclasses.dataclass
#@dataclasses.dataclass()                                       # alternative
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

이러한 기본값은 다음 키워드를 자동으로 설정하여 제공됩니다 True.

@dataclasses.dataclass(init=True, repr=True, eq=True)

당신이 켤 수있는 것

적절한 키워드가로 설정된 경우 추가 기능을 사용할 수 있습니다 True.

주문

@dataclasses.dataclass(order=True)
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

순서화 방법은 강력한 평등 테스트와 < > <= >=유사하게 구현됩니다 (과부하 연산자 functools.total_ordering:).

해시 가능, 가변

@dataclasses.dataclass(unsafe_hash=True)                        # override base `__hash__`
class Color:
    ...

객체는 잠재적으로 변경 가능하지만 (바람직하지 않을 수도 있음) 해시가 구현됩니다.

해시 가능, 불변

@dataclasses.dataclass(frozen=True)                             # `eq=True` (default) to be immutable 
class Color:
    ...

이제 해시가 구현되었으며 객체를 변경하거나 속성에 할당 할 수 없습니다.

전반적으로, unsafe_hash=True또는 경우 객체는 해시 가능합니다 frozen=True.

자세한 내용은 원래 해싱 논리 테이블 을 참조하십시오.

당신이 얻지 못한 것

다음 기능을 사용하려면 특수 메소드를 수동으로 구현해야합니다.

포장 풀기

@dataclasses.dataclass
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

    def __iter__(self):
        yield from dataclasses.astuple(self)

최적화

@dataclasses.dataclass
class SlottedColor:
    __slots__ = ["r", "b", "g"]
    r : int
    g : int
    b : int

이제 객체 크기가 줄어 듭니다.

>>> imp sys
>>> sys.getsizeof(Color)
1056
>>> sys.getsizeof(SlottedColor)
888

경우에 따라 __slots__인스턴스 작성 및 속성 액세스 속도도 향상됩니다. 또한 슬롯은 기본 할당을 허용하지 않습니다. 그렇지 않으면 a ValueError가 발생합니다.

블로그 게시물의 슬롯에 대해 자세히 알아보십시오 .


요약표

+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
|       Feature        |       Keyword        |                      Example                       |           Implement in a Class          |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
| Attributes           |  init                |  Color().r -> 0                                    |  __init__                               |
| Representation       |  repr                |  Color() -> Color(r=0, g=0, b=0)                   |  __repr__                               |
| Comparision*         |  eq                  |  Color() == Color(0, 0, 0) -> True                 |  __eq__                                 |
|                      |                      |                                                    |                                         |
| Order                |  order               |  sorted([Color(0, 50, 0), Color()]) -> ...         |  __lt__, __le__, __gt__, __ge__         |
| Hashable             |  unsafe_hash/frozen  |  {Color(), {Color()}} -> {Color(r=0, g=0, b=0)}    |  __hash__                               |
| Immutable            |  frozen + eq         |  Color().r = 10 -> TypeError                       |  __setattr__, __delattr__               |
|                      |                      |                                                    |                                         |
| Unpacking+           |  -                   |  r, g, b = Color()                                 |   __iter__                              |
| Optimization+        |  -                   |  sys.getsizeof(SlottedColor) -> 888                |  __slots__                              |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+

+ 이러한 메소드는 자동으로 생성되지 않으며 데이터 클래스에서 수동 구현이 필요합니다.

* __ne__ 는 필요 하지 않으므로 구현되지 않습니다 .


추가 기능

초기화 후

@dataclasses.dataclass
class RGBA:
    r : int = 0
    g : int = 0
    b : int = 0
    a : float = 1.0

    def __post_init__(self):
        self.a : int =  int(self.a * 255)


RGBA(127, 0, 255, 0.5)
# RGBA(r=127, g=0, b=255, a=127)

계승

@dataclasses.dataclass
class RGBA(Color):
    a : int = 0

전환

재귀 적 으로 데이터 클래스를 튜플 또는 dict로 변환하십시오 .

>>> dataclasses.astuple(Color(128, 0, 255))
(128, 0, 255)
>>> dataclasses.asdict(Color(128, 0, 255))
{r: 128, g: 0, b: 255}

한계


참고 문헌

  • R. Hettinger의 이야기Dataclasses : 모든 코드 생성기를 종료하는 코드 생성기
  • T. Hunner의 더 쉬운 클래스 에 대한 이야기 : 모든 크 루프트가없는 파이썬 클래스
  • 해싱 세부 사항에 대한 파이썬 문서
  • 실제 파이썬의 가이드파이썬 3.7에서 데이터 클래스에 궁극적 인 가이드
  • A. 쇼의 블로그 게시물파이썬 3.7 데이터 클래스의 개요 투어
  • E. 데이터 클래스 에 대한 Smith의 github 저장소

2

로부터 PEP 사양 :

PEP 526, "변수 주석 구문"에 정의 된 형식 주석이있는 변수에 대한 클래스 정의를 검사하는 클래스 데코레이터가 제공됩니다. 이 문서에서는 이러한 변수를 필드라고합니다. 데코레이터는 이러한 필드를 사용하여 생성 된 메소드 정의를 클래스에 추가하여 인스턴스 초기화, repr, 비교 메소드 및 선택적으로 스펙 섹션에 설명 된 기타 메소드를 지원합니다. 이러한 클래스를 데이터 클래스라고하지만 클래스에는 특별한 점이 없습니다. 데코레이터는 생성 된 메소드를 클래스에 추가하고 주어진 클래스와 동일한 클래스를 반환합니다.

@dataclass발전기는 달리처럼 자신을 정의하는 거라고 클래스에 메소드를 추가 __repr__, __init__, __lt__,와 __gt__.


2

이 간단한 수업을 고려하십시오 Foo

from dataclasses import dataclass
@dataclass
class Foo:    
    def bar():
        pass  

다음은 dir()기본 제공 비교입니다. 왼쪽 Foo에는 @dataclass 데코레이터가없고 오른쪽에는 @dataclass 데코레이터가 있습니다.

여기에 이미지 설명을 입력하십시오

inspect비교 를 위해 모듈을 사용한 후 또 다른 차이점이 있습니다.

여기에 이미지 설명을 입력하십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.