@dataclass
는 Python에서 클래스를 더 간단하고 가독성 있게 작성할 수 있게 해주는 데코레이터입니다. 일반적인 클래스에서는 __init__
, __repr__
, __eq__
같은 메서드를 직접 구현해야 하지만, @dataclass
는 이를 자동으로 생성해줍니다. 특히, 데이터 중심 클래스에 유용하게 사용할 수 있습니다.
Python에서 클래스를 작성할 때, 우리는 종종 많은 보일러플레이트 코드를 작성하게 됩니다. 예를 들어, 객체를 초기화하기 위한 __init__
메서드, 객체를 사람이 읽기 좋은 형식으로 출력하는 __repr__
, 객체 간 동등성 비교를 위한 __eq__
등을 매번 작성하는 것은 번거로울 수 있습니다.
이런 상황에서 Python의 @dataclass
데코레이터는 매우 유용한 도구입니다. 이 데코레이터는 클래스 정의를 간단하게 하고, 필요한 메서드들을 자동으로 생성해 줍니다.
@dataclass
란?
@dataclass
는 Python 3.7에서 도입된 데코레이터로, 데이터를 저장하기 위한 클래스에서 기본적으로 필요한 기능들을 자동으로 생성해 줍니다. 이를 통해 클래스 정의가 더 간결해지고 유지보수가 쉬워집니다.
다음 예시 코드를 통해 @dataclass
의 기본 사용법을 살펴보겠습니다.
기본 사용법
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
# Person 객체 생성
p1 = Person(name= "Alice" , age= 30 )
p2 = Person(name= "Bob" , age= 25 )
print (p1) # Person(name='Alice', age=30)
print (p2) # Person(name='Bob', age=25)
Person(name='Alice', age=30)
Person(name='Bob', age=25)
위 코드를 보면 __init__
, __repr__
같은 메서드를 명시적으로 작성하지 않았음에도 불구하고, Person
클래스의 인스턴스를 쉽게 생성하고 출력할 수 있습니다.
자동 생성되는 메서드
@dataclass
데코레이터를 사용하면 Python은 몇 가지 메서드를 자동으로 생성해 줍니다:
__init__
: 클래스 변수를 인자로 받아 객체를 초기화하는 메서드를 자동으로 생성합니다.
__repr__
: 객체를 사람이 읽기 좋은 문자열로 반환합니다.
__eq__
: 두 객체가 같은지 비교할 수 있게 해줍니다.
필드 기본값 설정
클래스 필드에 기본값을 설정하고 싶다면, 이를 명시적으로 지정할 수 있습니다. 기본값이 있는 필드는 항상 기본값이 없는 필드 뒤에 와야 합니다.
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int = 25 # 기본값 설정
p1 = Person(name= "Alice" )
p2 = Person(name= "Bob" , age= 30 )
print (p1) # Person(name='Alice', age=25)
print (p2) # Person(name='Bob', age=30)
Person(name='Alice', age=25)
Person(name='Bob', age=30)
field()
함수 사용
@dataclass
는 field()
함수를 사용하여 보다 세부적으로 필드를 설정할 수 있습니다. 이를 통해 기본값 설정이나, 필드의 기본 동작을 커스터마이징할 수 있습니다.
from dataclasses import dataclass, field
@dataclass
class Person:
name: str
age: int = field(default= 25 )
p = Person(name= "Alice" )
print (p) # Person(name='Alice', age=25)
Person(name='Alice', age=25)
field()
함수는 Python의 dataclasses
모듈에서 사용되는 도구로, @dataclass
데코레이터를 사용할 때 클래스 필드의 기본 동작을 세밀하게 제어할 수 있도록 도와줍니다. 이를 통해 필드에 대한 기본값, 초기화 방식, 메타데이터 등의 설정을 할 수 있습니다.
field()
는 특히 기본값이나 default_factory
를 사용하여 가변형 데이터(예: 리스트, 딕셔너리 등)의 기본값을 설정할 때 많이 사용되며, 필드가 자동으로 __init__
에 포함되는지 여부를 조절할 수 있는 다양한 옵션도 제공합니다.
field()
함수 사용법
다음은 field()
의 주요 인자와 그 사용 예시입니다.
1. default
: 기본값 설정
default
인자를 사용하면 필드의 기본값을 설정할 수 있습니다. 이는 @dataclass
에서 일반적인 필드 기본값 지정 방법과 동일하지만, field()
를 사용하면 더 세밀한 설정이 가능합니다.
from dataclasses import dataclass, field
@dataclass
class Person:
name: str
age: int = field(default= 25 ) # 기본값 25 설정
p = Person(name= "Alice" )
print (p) # Person(name='Alice', age=25)
Person(name='Alice', age=25)
2. default_factory
: 가변형 데이터 타입의 기본값 설정
가변형 데이터(리스트, 딕셔너리 등)의 기본값을 설정할 때는 default_factory
를 사용해야 합니다. 이는 각 객체가 고유의 가변 데이터를 가질 수 있게 해줍니다.
from dataclasses import dataclass, field
@dataclass
class ShoppingCart:
items: list = field(default_factory= list ) # 각 인스턴스에서 새로운 리스트 생성
cart1 = ShoppingCart()
cart2 = ShoppingCart()
cart1.items.append("Apple" )
cart2.items.append("Banana" )
print (cart1.items) # ['Apple']
print (cart2.items) # ['Banana']
여기서 default_factory=list
는 ShoppingCart
의 각 인스턴스가 독립된 리스트를 가지게 하여, cart1
과 cart2
의 아이템이 서로 영향을 주지 않게 합니다.
만약 default
를 사용해 리스트 같은 가변형 타입의 기본값을 설정하면 모든 인스턴스가 같은 리스트를 공유하게 되는 문제가 발생할 수 있습니다.
3. init
: __init__
메서드에 포함 여부 제어
init=False
로 설정하면 해당 필드가 __init__
메서드의 인자로 포함되지 않도록 할 수 있습니다. 즉, 이 필드는 객체 생성 시 외부에서 값을 넘길 수 없고, 클래스 내부에서만 값을 설정해야 합니다.
from dataclasses import dataclass, field
@dataclass
class Person:
name: str
age: int = field(init= False ) # __init__ 메서드에 포함되지 않음
def __post_init__(self ):
self .age = 30 # 초기화 이후에 값 설정
p = Person(name= "Alice" )
print (p) # Person(name='Alice', age=30)
Person(name='Alice', age=30)
위 예시에서 age
필드는 init=False
로 설정되어 __init__
에서 직접 초기화되지 않으며, __post_init__
메서드에서 값을 설정합니다.
4. repr
: 출력 여부 제어
repr=False
로 설정하면 __repr__
메서드에서 해당 필드를 생략할 수 있습니다. 이는 클래스의 출력 형식을 커스터마이징할 때 유용합니다.
from dataclasses import dataclass, field
@dataclass
class Person:
name: str
age: int = field(repr = False ) # __repr__ 메서드에서 출력하지 않음
p = Person(name= "Alice" , age= 30 )
print (p) # Person(name='Alice')
위 코드에서 age
필드는 repr=False
로 설정되어 print()
시 출력되지 않습니다.
5. compare
: 비교 메서드에서 사용 여부 제어
compare=False
로 설정하면 해당 필드가 __eq__
및 __lt__
, __gt__
등 비교 메서드에서 제외됩니다. 이를 통해 특정 필드를 무시한 객체 간 비교가 가능합니다.
from dataclasses import dataclass, field
@dataclass
class Person:
name: str
age: int = field(compare= False ) # 비교 메서드에서 제외
p1 = Person(name= "Alice" , age= 30 )
p2 = Person(name= "Alice" , age= 40 )
print (p1 == p2) # True, 나이를 비교하지 않음
위 코드에서 age
필드는 compare=False
로 설정되어 두 객체를 비교할 때 무시됩니다.
__post_init__
메서드
__init__
메서드가 실행된 후에 추가적인 초기화를 하고 싶다면 __post_init__
메서드를 사용할 수 있습니다. 이 메서드는 모든 필드가 초기화된 후에 호출됩니다.
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
def __post_init__(self ):
self .age += 1 # 나이를 1살 더함
p = Person(name= "Alice" , age= 30 )
print (p) # Person(name='Alice', age=31)
Person(name='Alice', age=31)
요약
@dataclass
는 Python에서 클래스를 정의할 때 불필요한 보일러플레이트 코드를 줄여주는 강력한 도구입니다. 특히, 데이터를 저장하기 위한 단순한 클래스에서 매우 유용하게 사용할 수 있습니다. 이를 통해 코드의 가독성을 높이고 유지보수를 쉽게 할 수 있습니다.