Python의 데이터 클래스 상속

Jay Shaw 2023년10월10일
  1. 파이썬의 상속
  2. Python의 다단계 상속
  3. Python에서 데이터 클래스 상속을 사용하여 기본 클래스와 하위 클래스 간에 기본 속성과 기본 속성이 아닌 속성 혼합
  4. 결론
Python의 데이터 클래스 상속

버전 3.7 이상에서는 Python에서 데이터 클래스 상속을 도입했습니다. 이 기사에서는 다단계 상속과 Python에서 데이터 클래스 상속을 사용하는 방법을 광범위하게 설명합니다.

파이썬의 상속

Python의 데이터 클래스 상속은 상위 클래스에서 하위 클래스의 데이터를 가져오는 데 사용되므로 반복되는 코드를 줄이고 코드를 재사용할 수 있습니다.

상속의 예를 살펴보겠습니다.

프로그램은 장식된 클래스를 생성할 수 있도록 dataclass 라이브러리 패키지를 가져옵니다. 여기에서 생성된 첫 번째 클래스는 Parent이며, 여기에는 문자열 name과 정수 age라는 두 가지 멤버 메서드가 있습니다.

그러면 여기에 Parent의 하위 클래스가 생성됩니다. Child 클래스는 school이라는 새 멤버 메서드를 도입합니다.

클래스에 두 개의 인수를 전달하는 클래스 Parent에 대해 인스턴스 객체 jack이 생성됩니다. 또 다른 인스턴스 개체인 jack_sonChild 클래스에 대해 만들어집니다.

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()의 인스턴스가 됩니다. 마지막으로 개체 obprint_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에 대한 jackChild에 대한 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를 사용하면 Parentname, ageugly로 시작하고 Child는 해당 목록 끝에 school을 추가합니다(ugly는 이미 목록).

그 결과 목록에 name, age, uglyschool이 있고 school에는 기본값이 없기 때문에 __init__ 함수는 결과를 잘못된 매개변수로 나열합니다.

@dataclass 데코레이터는 새 데이터 클래스를 만들 때 역방향 MRO(객체에서 시작)에서 클래스의 모든 기본 클래스를 검색하고 각 기본 클래스의 필드를 각 데이터 클래스에 대한 정렬된 필드 매핑에 추가합니다. 찾습니다.

그런 다음 모든 기본 클래스 필드가 추가된 후 정렬된 매핑에 해당 필드를 추가합니다. 이렇게 결합된 계산된 정렬된 필드 매핑은 생성된 모든 메서드에서 사용됩니다.

필드 배열로 인해 파생 클래스가 기본 클래스를 대체합니다.

기본값이 없는 필드가 기본값이 있는 필드 뒤에 오는 경우 TypeError가 생성됩니다. 이는 단일 클래스에서 발생하든 클래스 상속으로 인해 발생하든 마찬가지입니다.

이 문제를 해결하기 위한 첫 번째 대안은 다른 기본 클래스를 사용하여 기본값이 있는 필드를 MRO 순서의 나중 위치로 강제 지정하는 것입니다. 어떤 대가를 치르더라도 Parent와 같이 기본 클래스로 사용될 클래스에 필드를 직접 설정하지 마십시오.

이 프로그램에는 필드가 있는 기본 클래스가 있으며 기본값이 없는 필드는 구분됩니다. 공용 클래스는 base-withbase-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는 올바른 주문 유형을 이행하기 위해 마지막으로 유지됩니다.

기본값이 없는 필드가 있는 ParentBaseChildBase 클래스가 기본값이 있는 필드가 있는 ParentDefaultsBaseChildDefaultsBase 클래스 앞에 오기 때문에 데이터 클래스 규칙도 충족됩니다.

결과적으로 Child는 여전히 Parent의 하위 클래스인 반면 ParentChild 클래스는 올바른 필드 순서를 가집니다.

# __ Program Above __

print(signature(Parent))
print(signature(Child))

출력:

서명 함수

결론

이 기사에서는 Python의 데이터 클래스 상속에 대해 자세히 설명합니다. 데이터 클래스, 자식 클래스, 다단계 상속, 기본 클래스에서 하위 클래스로의 특성 혼합과 같은 개념을 자세히 설명합니다.

관련 문장 - Python Class

관련 문장 - Python Dataclass