dataclass
- 쉽고 간편한 자료형인 내장 자료구조들은 실행중 오류가 발생할 수 있다.
- 클래스를 직접 선언하고 데이터를 담아둔다면 type-safe하다.
기존방식
class User:
def __init__(self, name: str, age: int, password: str):
self.name = name
self.age = age
self.password = password
def __repr__(self):
return (
self.__class__.__name__
+ f"(name={self.name},age={self.age},password={self.password})"
)
user1 = User("steve", 20, "secret")
print(user1)
User(name=steve,age=20,password=secret)
class User:
def __init__(self, name: str, age: int, password: str):
self.name = name
self.age = age
self.password = password
def __eq__(self, other: object) -> bool:
if self.__class__ == other.__class__:
if (
self.name == other.name
and self.age == other.age
and self.password == other.password
):
return True
return False
user1 = User("steve", 20, "secret")
user2 = User("steve", 20, "secret")
user3 = User("david", 20, "secret")
print(user1 == user2)
print(user2 == user3)
True
False
- 객체를 참조하는데 있어서 init을 통해 속성을 설정하고
repr를 통해 객체를 쉽게 설명하고 eq을 통해 동등성을 수정해주었다.이는 dataclass를 사용하면 쉽게 기능들을 사용할 수 있다.
dataclass 사용
- 데코레이터를 선언한다.
- init(), repr(), eq()등과 같은 매직 메서드를 자동으로 생성해준다.
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
password: str
user1 = User("steve", 20, "secret")
user2 = User("steve", 20, "secret")
user3 = User("david", 20, "secret")
print(user1)
print(user1 == user2)
print(user2 == user3)
User(name='steve', age=20, password='secret')
True
False
from dataclasses import dataclass
@dataclass(frozen=True)
class User:
name: str
age: int
password: str
user1 = User("steve", 20, "secret")
user1.name = "david"
dataclasses.FrozenInstanceError: cannot assign to field 'name'
- dataclass는 기본적으로 unhashable하다.
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
password: str
user1 = User("steve", 20, "secret")
user2 = User("david", 23, "secret")
set1 = set([user1, user1, user2, user2])
print(set1)
TypeError: unhashable type: 'User'
- unsafe_hash옵션을 사용하면 hashable하게 변경된다.
from dataclasses import dataclass
@dataclass(unsafe_hash=True)
class User:
name: str
age: int
password: str
user1 = User("steve", 20, "secret")
user2 = User("david", 23, "secret")
set1 = set([user1, user1, user2, user2])
print(set1)
{User(name='steve', age=20, password='secret'), User(name='david', age=23, password='secret')}
oop적용 dataclass
from dataclasses import dataclass
@dataclass
class User:
__slots__ = ["_name", "_age", "_password"]
_name: str
_age: int
_password: str
@property
def name(self):
return self._name
@name.setter
def name(self, name: str):
self._name = name
@property
def age(self):
return self._name
@age.setter
def age(self, age: int):
self._age = age
@property
def password(self):
return self.password
@password.setter
def password(self, password: str):
self._password = password
user1 = User("steve", 20, "secret")
print(user1.name)
user1.name = "david"
print(user1.name)
user2 = User("david", 20, "secret")
print(user1 == user2)
user3 = User("john", 20, "secret")
print(user1 == user3)
steve
david
True
False
slot을 적용한 dataclass 성능
- slot을 적용한 dataclass가 메모리와 실행시간이 유리하다.
from dataclasses import dataclass
from memory_profiler import profile
import datetime
import time
@dataclass
class NoSlotUser:
_name: str
_age: int
_password: str
@property
def name(self):
return self._name
@name.setter
def name(self, name: str):
self._name = name
@property
def age(self):
return self._name
@age.setter
def age(self, age: int):
self._age = age
@property
def password(self):
return self.password
@password.setter
def password(self, password: str):
self._password = password
@dataclass
class WithSlotUser:
__slots__ = ["_name", "_age", "_password"]
_name: str
_age: int
_password: str
@property
def name(self):
return self._name
@name.setter
def name(self, name: str):
self._name = name
@property
def age(self):
return self._name
@age.setter
def age(self, age: int):
self._age = age
@property
def password(self):
return self.password
@password.setter
def password(self, password: str):
self._password = password
loop = 1000000
print("==============withSlot=========== 메모리")
@profile
def func1():
obList1 = [WithSlotUser("test", 10, "test_password") for i in range(loop)]
del obList1
func1()
print("==============NoSlot=========== 메모리")
@profile
def func2():
obList2 = [NoSlotUser("test", 10, "test_password") for i in range(loop)]
del obList2
func2()
print("==============withSlot=========== 실행시간")
def func3():
for i in range(loop):
obList3 = WithSlotUser("test", 10, "test_password")
del obList3
start = time.time()
func3()
end = time.time()
sec = end - start
result = datetime.timedelta(seconds=sec)
print(result)
print("==============noSlot=========== 실행시간")
def func4():
for i in range(loop):
obList4 = NoSlotUser("test", 10, "test_password")
del obList4
start = time.time()
func4()
end = time.time()
sec = end - start
result = datetime.timedelta(seconds=sec)
print(result)
==============withSlot=========== 메모리
Line # Mem usage Increment Occurrences Line Contents
=============================================================
75 20.7 MiB 20.7 MiB 1 @profile
76 def func1():
77 89.6 MiB 68.9 MiB 1000003 obList1 = [WithSlotUser("test", 10, "test_password") for i in range(loop)]
78 31.1 MiB -58.5 MiB 1 del obList1
==============NoSlot=========== 메모리
Line # Mem usage Increment Occurrences Line Contents
=============================================================
85 31.1 MiB 31.1 MiB 1 @profile
86 def func2():
87 188.7 MiB 157.6 MiB 1000003 obList2 = [NoSlotUser("test", 10, "test_password") for i in range(loop)]
88 38.2 MiB -150.5 MiB 1 del obList2
==============withSlot=========== 실행시간
0:00:00.137285
==============noSlot=========== 실행시간
0:00:00.154017