Python Dict 대 Asdict
dataclasses
라이브러리는 Python 3.7에서 도입되어 데이터 저장을 위해 특별히 구조화된 클래스를 만들 수 있습니다. 이러한 클래스에는 데이터와 해당 묘사를 처리하는 특정 속성과 메서드가 있습니다.
Python의 dataclasses
라이브러리
dataclasses
라이브러리를 설치하려면 아래 명령을 사용하십시오.
pip install dataclasses
Python의 일반 클래스와 달리 dataclasses
는 클래스와 함께 @dataclass
데코레이터를 사용하여 구현됩니다. 또한 속성 선언은 dataclass
의 속성에 대한 데이터 유형을 지정하는 유형 힌트를 사용하여 작성됩니다.
아래는 개념을 실제로 적용하는 코드 스니펫입니다.
# A bare-bones Data Class
# Don't forget to import the dataclass module
from dataclasses import dataclass
@dataclass
class Student:
"""A class which holds a students data"""
# Declaring attributes
# Making use of type hints
name: str
id: int
section: str
classname: str
fatherName: str
motherName: str
# Below is a dataclass instance
student = Student("Muhammad", 1432, "Red", "0-1", "Ali", "Marie")
print(student)
출력:
Student(name='Muhammad', id=1432, section='Red', classname='0-1', fatherName='Ali', motherName='Marie')
위의 코드에서 주목해야 할 두 가지 사항이 있습니다. 첫째, dataclass
개체는 인수를 수락하고 _init_()
생성자 없이 관련 데이터 멤버에 할당합니다.
이는 dataclass
가 내장 _init_()
생성자를 제공하기 때문입니다.
두 번째로 주목해야 할 점은 print
문이 이를 수행하도록 특별히 프로그래밍된 기능 없이 개체에 있는 데이터를 깔끔하게 인쇄한다는 것입니다. 이는 변경된 _repr_()
함수가 있어야 함을 의미합니다.
dict
가 asdict
보다 빠른 이유
대부분의 경우 데이터 클래스 없이 dict
를 사용했을 경우 확실히 dict
를 계속 사용해야 합니다.
그러나 asdict
는 복사 호출 중에 사용자의 경우에 유용하지 않을 수 있는 추가 작업을 수행합니다. 이러한 추가 작업에는 피하고 싶은 오버헤드가 있습니다.
공식 문서에 따르면 다음과 같습니다. 각 dataclass
개체는 먼저 해당 필드의 dict
로 name: value
쌍으로 변환됩니다.
그런 다음 dataclasses
, dicts
, 목록 및 튜플이 재귀됩니다.
예를 들어 재귀적인 dataclass
받아쓰기가 필요한 경우 asdict
로 이동합니다. 그렇지 않으면 이를 제공하는 데 들어가는 모든 추가 작업이 낭비됩니다.
특히 asdict
를 사용하는 경우 dataclass
를 사용하도록 포함된 개체의 구현을 수정하면 외부 개체에서 asdict
의 결과가 변경됩니다.
from dataclasses import dataclass, asdict
from typing import List
@dataclass
class APoint:
x1: int
y1: int
@dataclass
class C:
aList: List[APoint]
point_instance = APoint(10, 20)
assert asdict(point_instance) == {"x1": 10, "y1": 20}
c = C([APoint(30, 40), APoint(50, 60)])
assert asdict(c) == {"aList": [{"x1": 30, "y1": 40}, {"x1": 50, "y1": 60}]}
더욱이 재귀적 비즈니스 논리는 순환 참조를 처리할 수 없습니다. 예를 들어 그래프나 순환 참조가 있는 다른 데이터 구조를 나타내기 위해 dataclasses
를 사용하는 경우 asdict
가 확실히 중단됩니다.
@dataclasses.dataclass
class GraphNode:
name: str
neighbors: list["GraphNode"]
x = GraphNode("x", [])
y = GraphNode("y", [])
x.neighbors.append(y)
y.neighbors.append(x)
dataclasses.asdict(x)
# The code will crash here as
# the max allowed recursion depth would have exceeded
# while calling the python object
# in case you're running this on jupyter notebook notice
# that the kernel will restart as the code crashed
또한 asdict
는 개체의 dict
속성에 직접 액세스하지만 __dict__
인 새 dict
를 빌드합니다.
asdict
의 반환 값은 원래 개체 속성의 재할당에 의해 어떤 식으로든 영향을 받지 않는다는 점에 유의해야 합니다.
또한 선언된 필드에 매핑되지 않는 dataclass
객체에 속성을 추가하는 경우 asdict
가 필드를 사용한다는 점을 고려하면 asdict
는 해당 필드를 포함하지 않습니다.
마지막으로 문서에서 명시적으로 언급하지 않더라도 asdict
는 dataclass
인스턴스, dict
, 목록 또는 튜플이 아닌 모든 항목에 대해 딥 카피를 호출합니다.
return copy.deepcopy(instance) # a very costly operation !
Dataclass
인스턴스, dicts
, 목록 및 튜플은 재귀 논리를 거치며 재귀 받아쓰기가 적용된 사본을 추가로 작성합니다.
객체 지향 패러다임에 상당히 정통한 경우 딥 복사는 모든 객체를 검사하여 무엇을 복사해야 하는지 확인하기 때문에 그 자체로 비용이 많이 드는 작업임을 알 수 있습니다. 메모 처리가 없다는 것은 본질적으로 asdict
가 사소하지 않은 개체 그래프에서 공유 개체의 여러 복사본을 만들 수 있음을 의미합니다.
다음과 같은 시나리오에 주의하세요.
from dataclasses import dataclass, asdict
@dataclass
class PointClass:
x1: object
y1: object
obj_instance = object()
var1 = PointClass(obj_instance, obj_instance)
var2 = asdict(var1)
print(var1.x1 is var1.y1) # prints true
print(var2["x1"] is var2["y1"]) # prints false
print(var2["x1"] is var1.x1) # prints false
출력:
True
False
False