Python의 데이터 클래스 상속
버전 3.7 이상에서는 Python에서 데이터 클래스 상속을 도입했습니다. 이 기사에서는 다단계 상속과 Python에서 데이터 클래스 상속을 사용하는 방법을 광범위하게 설명합니다.
파이썬의 상속
Python의 데이터 클래스 상속은 상위 클래스에서 하위 클래스의 데이터를 가져오는 데 사용되므로 반복되는 코드를 줄이고 코드를 재사용할 수 있습니다.
상속의 예를 살펴보겠습니다.
프로그램은 장식된 클래스를 생성할 수 있도록 dataclass
라이브러리 패키지를 가져옵니다. 여기에서 생성된 첫 번째 클래스는 Parent
이며, 여기에는 문자열 name
과 정수 age
라는 두 가지 멤버 메서드가 있습니다.
그러면 여기에 Parent
의 하위 클래스가 생성됩니다. Child
클래스는 school
이라는 새 멤버 메서드를 도입합니다.
클래스에 두 개의 인수를 전달하는 클래스 Parent
에 대해 인스턴스 객체 jack
이 생성됩니다. 또 다른 인스턴스 개체인 jack_son
은 Child
클래스에 대해 만들어집니다.
Child
클래스는 Parent
의 하위 클래스이므로 데이터 멤버는 Child
클래스에서 파생될 수 있습니다. 이것이 Python에서 데이터 클래스 상속의 주요 특징입니다.
from dataclasses import dataclass
@dataclass
class Parent:
name: str
age: int
def print_name(self):
print(f"Name is '{self.name}' and age is= {self.age}")
@dataclass
class Child(Parent):
school: str
jack = Parent("Jack snr", 35)
jack_son = Child("Jack jnr", 12, school="havard")
jack_son.print_name()
출력:
C:\python38\python.exe "C:/Users/Win 10/PycharmProjects/class inheritance/2.py"
Name is 'Jack jnr' and age is= 12
Process finished with exit code 0
Python의 다단계 상속
Python에서 데이터 클래스 상속이 어떻게 작동하는지 살펴보았으므로 이제 다단계 상속 개념을 살펴보겠습니다. 부모 클래스에서 생성된 하위 클래스가 후속 손자 클래스의 부모로 사용되는 상속 유형입니다.
아래 예는 단순한 형태의 다단계 상속을 보여줍니다.
부모 클래스
Parent
클래스는 생성자 __init__
및 멤버 메서드 print_method
를 사용하여 생성됩니다. 생성자는 "Initialized in Parent"
문을 인쇄하여 클래스가 자식 클래스에서 호출될 때 표시되도록 합니다.
print_method
함수에는 이 메소드가 호출될 때 인쇄되는 매개변수 b
가 있습니다.
차일드 클래스
Child
클래스는 Parent
에서 파생되며 생성자 내부에 명령문을 인쇄합니다. super().__init__
는 자식 클래스가 있는 기본 클래스를 나타냅니다.
우리는 super()
를 사용하여 자식 클래스가 사용하는 잠재적 협력 다중 상속이 Method Resolution Order (MRO)에서 적절한 다음 부모 클래스 함수를 호출하도록 합니다.
print_method
멤버 메서드가 오버로드되고 print 문이 b
값을 인쇄합니다. 여기서도 super()
는 부모 클래스의 멤버 메서드를 나타냅니다.
그랜드 차일드 클래스
이 시점에서 프로그램은 Child
클래스에서 상속된 또 다른 클래스를 만들기 위해 구조를 상용구(반복 코드)로 만듭니다. print_method
내부에서 b
값은 super()
를 사용하여 증가합니다.
주요 기능
마지막으로 main
함수가 생성되어 ob
객체를 생성하고 GrandChild()
의 인스턴스가 됩니다. 마지막으로 개체 ob
는 print_method
를 호출합니다.
파이썬에서 데이터 클래스 상속을 사용할 때 다단계 클래스가 쌓이는 방식입니다.
class Parent:
def __init__(self):
print("Initialized in Parent")
def print_method(self, b):
print("Printing from class Parent:", b)
class Child(Parent):
def __init__(self):
print("Initialized in Child")
super().__init__()
def print_method(self, b):
print("Printing from class Child:", b)
super().print_method(b + 1)
class GrandChild(Child):
def __init__(self):
print("Initialized in Grand Child")
super().__init__()
def print_method(self, b):
print("Printing from class Grand Child:", b)
super().print_method(b + 1)
if __name__ == "__main__":
ob = GrandChild()
ob.print_method(10)
출력:
C:\python38\python.exe "C:/Users/Win 10/PycharmProjects/class inheritance/3.py"
Initialized in Grand Child
Initialized in Child
Initialized in Parent
Printing from class Grand Child: 10
Printing from class Child: 11
Printing from class Parent: 12
Process finished with exit code 0
여기서 코드가 수행하는 작업을 이해해 보겠습니다.
main
함수는 Grand Child
클래스의 print_method
함수에 10
값을 전달합니다. MRO(Method Resolution Order)에 따라 프로그램은 먼저 Grand Child
클래스를 실행하고 __init__
문을 인쇄한 다음 상위인 Child
클래스로 이동합니다.
Child
클래스와 Parent
클래스는 MRO에 따라 __init__
문을 인쇄한 다음 컴파일러는 GrandChild
클래스의 print_method
를 따릅니다. 이 메서드는 10
(b
의 값)을 인쇄한 다음 super()
를 사용하여 상위 클래스인 Child
클래스에서 b
의 값을 증가시킵니다.
그런 다음 컴파일러는 Child
클래스의 print_method
로 이동하여 11
을 인쇄합니다. 그런 다음 MRO의 마지막 수준에는 12
를 인쇄하는 Parent
클래스가 있습니다.
Parent
클래스 위에 수퍼클래스가 없으므로 프로그램이 종료됩니다.
Python의 데이터 클래스 상속에서 다단계 상속이 작동하는 방식을 이해했으므로 다음 섹션에서는 부모 클래스에서 속성을 상속하는 개념과 이를 수정하는 방법을 다룰 것입니다.
Python에서 데이터 클래스 상속을 사용하여 기본 클래스와 하위 클래스 간에 기본 속성과 기본 속성이 아닌 속성 혼합
Python의 데이터 클래스 상속에서 자식 클래스를 사용하여 부모 클래스의 데이터 멤버에 액세스하는 방법과 다단계 상속이 작동하는 방식을 살펴보았습니다. 이제 하위 클래스가 상위 클래스의 데이터 멤버에 액세스할 수 있는지 여부에 대한 질문이 발생합니다. 변경할 수 있습니까?
대답은 ‘예’이지만 TypeErrors를 피해야 합니다. 예를 들어, 아래 프로그램에는 Parent
클래스와 Child
하위 클래스의 두 클래스가 있습니다.
Parent
클래스에는 name
, age
및 기본적으로 False
로 설정된 bool 변수 ugly
의 세 가지 데이터 멤버가 있습니다. 세 가지 멤버 메서드는 name
, age
및 id를 인쇄합니다.
이제 Parent
에서 파생된 장식된 Child
클래스 내부에 school
이라는 새 데이터 멤버가 도입되었습니다. 이를 통해 클래스는 ugly
변수의 속성을 False
에서 True
로 변경합니다.
Parent
에 대한 jack
과 Child
에 대한 jack_son
의 두 개체가 생성됩니다. 이러한 개체는 클래스에 인수를 전달하고 두 개체 모두 print_id
메서드를 호출하고 세부 정보를 인쇄합니다.
이 메서드를 사용하여 기본 클래스의 기본값을 변경하는 데 있어 주요 문제는 TypeError가 발생한다는 것입니다.
from dataclasses import dataclass
@dataclass
class Parent:
name: str
age: int
ugly: bool = False
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f"ID: Name - {self.name}, age = {self.age}")
@dataclass
class Child(Parent):
school: str
ugly: bool = True
jack = Parent("jack snr", 32, ugly=True)
jack_son = Child("Mathew", 14, school="cambridge", ugly=True)
jack.print_id()
jack_son.print_id()
출력:
raise TypeError(f'non-default argument {f.name!r} '
TypeError: non-default argument 'school' follows default argument
이 오류의 원인은 Python의 데이터 클래스 상속에서 속성을 기본 클래스에서 기본값과 함께 사용할 수 없으며 데이터 클래스가 속성을 혼합하는 방식으로 인해 하위 클래스에서 기본값(위치 속성) 없이 사용할 수 없기 때문입니다.
이것은 속성이 MRO의 맨 아래에서 처음부터 병합되고 재정의가 원래 위치에 남아 있는 상태에서 처음 본 순서대로 속성의 정렬된 목록을 작성하기 때문입니다.
기본적으로 ugly
를 사용하면 Parent
는 name
, age
및 ugly
로 시작하고 Child
는 해당 목록 끝에 school
을 추가합니다(ugly
는 이미 목록).
그 결과 목록에 name
, age
, ugly
및 school
이 있고 school
에는 기본값이 없기 때문에 __init__
함수는 결과를 잘못된 매개변수로 나열합니다.
@dataclass
데코레이터는 새 데이터 클래스를 만들 때 역방향 MRO(객체에서 시작)에서 클래스의 모든 기본 클래스를 검색하고 각 기본 클래스의 필드를 각 데이터 클래스에 대한 정렬된 필드 매핑에 추가합니다. 찾습니다.
그런 다음 모든 기본 클래스 필드가 추가된 후 정렬된 매핑에 해당 필드를 추가합니다. 이렇게 결합된 계산된 정렬된 필드 매핑은 생성된 모든 메서드에서 사용됩니다.
필드 배열로 인해 파생 클래스가 기본 클래스를 대체합니다.
기본값이 없는 필드가 기본값이 있는 필드 뒤에 오는 경우 TypeError가 생성됩니다. 이는 단일 클래스에서 발생하든 클래스 상속으로 인해 발생하든 마찬가지입니다.
이 문제를 해결하기 위한 첫 번째 대안은 다른 기본 클래스를 사용하여 기본값이 있는 필드를 MRO 순서의 나중 위치로 강제 지정하는 것입니다. 어떤 대가를 치르더라도 Parent
와 같이 기본 클래스로 사용될 클래스에 필드를 직접 설정하지 마십시오.
이 프로그램에는 필드가 있는 기본 클래스가 있으며 기본값이 없는 필드는 구분됩니다. 공용 클래스는 base-with
및 base-without
클래스에서 파생됩니다.
public 클래스의 하위 클래스는 기본 클래스를 앞에 둡니다.
from dataclasses import dataclass
@dataclass
class _ParentBase:
name: str
age: int
@dataclass
class _ParentDefaultsBase:
ugly: bool = False
@dataclass
class _ChildBase(_ParentBase):
school: str
@dataclass
class _ChildDefaultsBase(_ParentDefaultsBase):
ugly: bool = True
@dataclass
class Parent(_ParentDefaultsBase, _ParentBase):
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f"ID: Name - {self.name}, age = {self.age}")
@dataclass
class Child(_ChildDefaultsBase, Parent, _ChildBase):
pass
Amit = Parent("Amit snr", 32, ugly=True)
Amit_son = Child("Amit jnr", 12, school="iit", ugly=True)
Amit.print_id()
Amit_son.print_id()
출력:
C:\python38\python.exe "C:/main.py"
The Name is Amit snr and Amit snr is 32 year old
The Name is Amit jnr and Amit jnr is 12 year old
Process finished with exit code 0
여기서 생성된 MRO는 필드를 “기본값 없음” 및 “기본값 있음” 필드가 있는 별도의 기본 클래스로 분할하고 신중하게 상속 순서를 선택하여 기본값이 있는 필드보다 기본값이 없는 필드를 우선시합니다. Child
의 MRO는 다음과 같습니다.
<class 'object'>
||
<class '__main__._ParentBase'>,
||
<class '__main__._ChildBase'>
||
<class '__main__._ParentDefaultsBase'>,
||
<class '__main__.Parent'>,
||
<class '__main__._ChildDefaultsBase'>,
||
<class '__main__._Child'>
Parent
는 새 필드를 만들지 않지만 ParentDefaultsBase
에서 필드를 상속하므로 필드 나열 순서에서 마지막에 오지 않아야 합니다. 따라서 ChildDefaultsBase
는 올바른 주문 유형을 이행하기 위해 마지막으로 유지됩니다.
기본값이 없는 필드가 있는 ParentBase
및 ChildBase
클래스가 기본값이 있는 필드가 있는 ParentDefaultsBase
및 ChildDefaultsBase
클래스 앞에 오기 때문에 데이터 클래스 규칙도 충족됩니다.
결과적으로 Child
는 여전히 Parent
의 하위 클래스인 반면 Parent
및 Child
클래스는 올바른 필드 순서를 가집니다.
# __ Program Above __
print(signature(Parent))
print(signature(Child))
출력:
결론
이 기사에서는 Python의 데이터 클래스 상속에 대해 자세히 설명합니다. 데이터 클래스, 자식 클래스, 다단계 상속, 기본 클래스에서 하위 클래스로의 특성 혼합과 같은 개념을 자세히 설명합니다.