Python의 모든 것은 객체입니다. 고유한 속성과 메서드를 사용하여 고유한 사용자 지정 개체를 만들려면 Python의 class
그렇게 되도록 반대합니다. 그러나 Python에서 클래스를 생성하는 것은 때때로 전달된 매개변수에서 클래스 인스턴스를 설정하거나 비교 연산자와 같은 공통 함수를 생성하기 위해 반복적인 상용구 코드를 작성하는 것을 의미합니다.
Python 3.7에 도입되고 Python 3.6으로 백포트된 데이터 클래스는 클래스를 생성하는 편리하고 덜 장황한 방법을 제공합니다. 클래스에 전달된 인수에서 속성을 인스턴스화하는 것과 같이 클래스에서 수행하는 많은 일반적인 작업은 몇 가지 기본 지침으로 줄일 수 있습니다.
Python 데이터 클래스 예제
다음은 Python의 기존 클래스에 대한 간단한 예입니다.
class Book:
'''Object for tracking physical books in a collection.'''
def __init__(self, name: str, weight: float, shelf_id:int = 0):
self.name = name
self.weight = weight # in grams, for calculating shipping
self.shelf_id = shelf_id
def __repr__(self):
return(f"Book(name={self.name!r},
weight={self.weight!r}, shelf_id={self.shelf_id!r})")
여기서 가장 큰 골칫거리는 각 인수가 전달되는 방식입니다. __init__
개체의 속성에 복사해야 합니다. 이것만 다루면 나쁘지 않다. Book
하지만 처리해야 하는 경우 Bookshelf
, Library
, Warehouse
, 등등? 또한 손으로 입력해야 하는 코드가 많을수록 실수할 가능성이 커집니다.
다음은 Python 데이터 클래스로 구현된 동일한 Python 클래스입니다.
from dataclasses import dataclass
@dataclass
class Book:
'''Object for tracking physical books in a collection.'''
name: str
weight: float
shelf_id: int = 0
속성을 지정할 때 필드, 데이터 클래스에서 @dataclass
데코레이터는 초기화에 필요한 모든 코드를 자동으로 생성합니다. 또한 각 속성에 대한 유형 정보를 유지하므로 다음과 같은 코드 린터를 사용하는 경우 mypy
클래스 생성자에 올바른 종류의 변수를 제공하고 있는지 확인합니다.
또 다른 한가지 @dataclass
장면 뒤에서는 클래스의 여러 일반적인 dunder 메서드에 대한 코드를 자동으로 생성합니다. 위의 기존 클래스에서 우리는 우리 자신을 만들어야 했습니다. __repr__
. 데이터 클래스에서 @dataclass
데코레이터는 __repr__
당신을 위한.
데이터 클래스가 생성되면 기능적으로 일반 클래스와 동일합니다. 데이터 클래스를 사용해도 성능이 저하되지 않습니다. 클래스를 데이터 클래스로 선언하면 약간의 성능 저하가 있을 뿐이며 이는 데이터 클래스 개체가 생성될 때 일회성 비용입니다.
고급 Python 데이터 클래스 초기화
데이터 클래스 데코레이터는 자체 초기화 옵션을 사용할 수 있습니다. 대부분의 경우 제공할 필요가 없지만 특정 경우에 유용할 수 있습니다. 다음은 가장 유용한 것 중 일부입니다(모두 True/False
):
frozen
: 읽기 전용인 클래스 인스턴스를 생성합니다. 데이터가 할당되면 수정할 수 없습니다.slots
: 클래스에 명시적으로 정의된 필드만 허용하여 데이터 클래스의 인스턴스가 더 적은 메모리를 사용하도록 허용합니다.kw_only
: 설정하면 클래스의 모든 필드가 키워드 전용입니다.
다음을 사용하여 Python 데이터 클래스 필드를 사용자 지정합니다. field
기능
데이터 클래스가 작동하는 기본 방식은 대부분의 사용 사례에 적합해야 합니다. 하지만 때로는 데이터 클래스의 필드가 초기화되는 방식을 미세 조정해야 합니다. 아래와 같이 사용할 수 있습니다. field
미세 조정 기능:
from dataclasses import dataclass, field
from typing import List
@dataclass
class Book:
'''Object for tracking physical books in a collection.'''
name: str
condition: str = field(compare=False)
weight: float = field(default=0.0, repr=False)
shelf_id: int = 0
chapters: List[str] = field(default_factory=list)
인스턴스에 기본값을 설정하는 경우 field
제공하는 매개변수에 따라 필드 설정 방법이 변경됩니다. field
. 다음은 가장 일반적으로 사용되는 옵션입니다. field
(다른 것들도 있습니다):
default
: 필드의 기본값을 설정합니다. 당신은 사용해야합니다default
당신이 a) 사용하는 경우field
필드에 대한 다른 매개변수를 변경하고 b) 그 위에 있는 필드에 기본값을 설정하려고 합니다. 이 경우, 우리는default
설정weight
에게0.0
.default_factory
: 매개 변수를 사용하지 않고 필드의 기본값으로 사용할 일부 개체를 반환하는 함수의 이름을 제공합니다. 이 경우, 우리는 원한다chapters
빈 목록이 됩니다.repr
: 기본적으로 (True
), 문제의 필드가 자동으로 생성된__repr__
데이터 클래스의 경우. 이 경우 우리는 책의 무게가__repr__
그래서 우리는 사용repr=False
그것을 생략하기 위해.compare
: 기본적으로 (True
), 데이터 클래스에 대해 자동으로 생성된 비교 메서드의 필드를 포함합니다. 여기, 우리는 원하지 않는다condition
두 권의 책에 대한 비교의 일부로 사용되므로 다음을 설정합니다.compare=False
.
기본 필드가 아닌 필드가 먼저 오도록 필드의 순서를 조정해야 했습니다.
Python 데이터 클래스 초기화 제어
이 시점에서 아마 궁금할 것입니다. __init__
데이터 클래스의 메서드가 자동으로 생성됩니다. 보다 세분화된 변경을 수행하기 위해 초기화 프로세스를 제어하려면 어떻게 해야 합니까?
__post_init__
들어가다 __post_init__
방법. 포함하는 경우 __post_init__
데이터 클래스 정의에서 메서드를 사용하여 필드 또는 기타 인스턴스 데이터를 수정하기 위한 지침을 제공할 수 있습니다.
from dataclasses import dataclass, field
from typing import List
@dataclass
class Book:
'''Object for tracking physical books in a collection.'''
name: str
weight: float = field(default=0.0, repr=False)
shelf_id: Optional[int] = field(init=False)
chapters: List[str] = field(default_factory=list)
condition: str = field(default="Good", compare=False)
def __post_init__(self):
if self.condition == "Discarded":
self.shelf_id = None
else:
self.shelf_id = 0
이 예에서는 __post_init__
설정 방법 shelf_id
에게 None
책의 상태가 다음과 같이 초기화된 경우 "Discarded"
. 우리가 사용하는 방법을 참고 field
초기화하다 shelf_id
패스 init
같이 False
에게 field
. 이것은 의미합니다 shelf_id
에서 초기화되지 않습니다 __init__
.
InitVar
Python 데이터 클래스 설정을 사용자 지정하는 또 다른 방법은 다음을 사용하는 것입니다. InitVar
유형. 이렇게 하면 전달될 필드를 지정할 수 있습니다. __init__
그리고 나서 __post_init__
그러나 클래스 인스턴스에 저장되지 않습니다.
사용하여 InitVar
, 초기화 중에만 사용되는 데이터 클래스를 설정할 때 매개 변수를 사용할 수 있습니다. 예를 들면 다음과 같습니다.
from dataclasses import dataclass, field, InitVar
from typing import List
@dataclass
class Book:
'''Object for tracking physical books in a collection.'''
name: str
condition: InitVar[str] = "Good"
weight: float = field(default=0.0, repr=False)
shelf_id: int = field(init=False)
chapters: List[str] = field(default_factory=list)
def __post_init__(self, condition):
if condition == "Unacceptable":
self.shelf_id = None
else:
self.shelf_id = 0
필드 유형을 다음으로 설정 InitVar
(하위 유형이 실제 필드 유형임)에 신호를 보냅니다. @dataclass
해당 필드를 데이터 클래스 필드로 만들지 않고 데이터를 __post_init__
인수로.
이번 버전에서는 Book
클래스, 우리는 저장하지 않습니다 condition
클래스 인스턴스의 필드로. 우리는 단지 사용하고 있습니다 condition
초기화 단계에서. 우리가 그것을 찾으면 condition
로 설정되었다 "Unacceptable"
우리는 설정 shelf_id
에게 None
— 하지만 우리는 저장하지 않습니다 condition
클래스 인스턴스에서 자체.
파이썬 데이터 클래스를 사용해야 할 때와 사용하지 말아야 할 때
데이터 클래스를 사용하는 일반적인 시나리오는 다음과 같습니다. namedtuple을 대체합니다. 데이터 클래스는 동일한 동작 등을 제공하며 변경할 수 없게 만들 수 있습니다(예: namedtuple
s는) 단순히 사용하여 @dataclass(frozen=True)
데코레이터로.
또 다른 가능한 사용 사례는 중첩된 사전 바꾸기, 데이터 클래스의 중첩된 인스턴스로 작업하기 어려울 수 있습니다. 데이터 클래스가 있는 경우 Library
목록 속성이 shelves
당신은 데이터 클래스를 사용할 수 있습니다 ReadingRoom
목록을 채운 다음 중첩된 항목(예: 특정 방의 선반에 있는 책)에 쉽게 액세스할 수 있도록 메서드를 추가합니다.
그러나 모든 Python 클래스가 데이터 클래스일 필요는 없습니다. 주로 데이터의 컨테이너가 아니라 여러 정적 메서드를 함께 그룹화하는 방법으로 클래스를 만드는 경우 데이터 클래스로 만들 필요가 없습니다. 예를 들어 파서의 일반적인 패턴은 추상 구문 트리를 가져와 트리를 탐색하고 노드 유형에 따라 클래스의 다른 메서드에 대한 호출을 전달하는 클래스를 갖는 것입니다. 파서 클래스에는 자체 데이터가 거의 없기 때문에 여기서 데이터 클래스는 유용하지 않습니다.