[Python] __init__() 속성 초기화와 super()의 중요성( 예제 + 실습)

Jimin_Note·7일 전
0

[Python]

목록 보기
37/40
post-thumbnail

init()의 기본 동작

class Calculator:

    def __init__(self, n1, n2):
        print('[Calculator] __init__() called!!')
        self.num1 = n1
        self.num2 = n2

cal = Calculator(10, 20)
print(f'cal.num1: {cal.num1}')
print(f'cal.num2: {cal.num2}')

<결과>

[Calculator] __init__() called!!
cal.num1: 10
cal.num2: 20

__init__()은 클래스의 생성자 함수로, 객체가 생성될 때 자동으로 호출되며 속성을 초기화하는 역할을 한다.

✅ 부모 클래스 상속했을 때의 동작

class P_Class:

    def __init__(self, p_num1, p_num2):
        print('[pClass] __init__()')

        self.p_num1 = p_num1
        self.p_num2 = p_num2

class C_Class(P_Class):
    def __init__(self, c_num1, c_num2):
        print('[cClass] __init__()')

        self.c_num1 = c_num1
        self.c_num2 = c_num2

cls = C_Class(10,20)

<결과>

[cClass] __init__()

[pClass] __init__()은 호출되지 않음!

자식 클래스는 초기화가 됐지만 부모 클래스는 초기화를 안해서 그렇다.
부모 클래스도 강제로 초기화를 해줘야함!

🔧 직접 호출 vs super() 사용

1️⃣ 직접 호출

class P_Class:

    def __init__(self, p_num1, p_num2):
        print('[pClass] __init__()')

        self.p_num1 = p_num1
        self.p_num2 = p_num2

class C_Class(P_Class):
    def __init__(self, c_num1, c_num2):
        print('[cClass] __init__()')
				
				P_Class.__init__(self,c_num1, c_num2) # 여기!

        self.c_num1 = c_num1
        self.c_num2 = c_num2

cls = C_Class(10,20)

<결과>

[cClass] __init__()
[pClass] __init__()

P_Class.__init__(self,c_num1, c_num2)

이렇게 부모 클래스를 직접 호출해서 사용해도 기능상 문제는 없지만 단점이 있기 때문에 super() 을 써주는 게 좋다.

단점을 아래에 정리해두었다.

2️⃣ super() 사용

class P_Class:

    def __init__(self, p_num1, p_num2):
        print('[pClass] __init__()')

        self.p_num1 = p_num1
        self.p_num2 = p_num2

class C_Class(P_Class):
    def __init__(self, c_num1, c_num2):
        print('[cClass] __init__()')
				
				super().__init__(c_num1, c_num2) # self 생략 가능

        self.c_num1 = c_num1
        self.c_num2 = c_num2

cls = C_Class(10,20)

<결과>

[cClass] __init__()
[pClass] __init__()

super()는 다중 상속에서도 안전하게 작동하며, MRO를 자동으로 따른다!

⁉️ 부모 클래스 직접 호출하여 강제 초기화 시의 단점

  1. 다중 상속에서 꼬일 수 있음.
class A:
    def __init__(self):
        print("A")

class B:
    def __init__(self):
        print("B")

class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)

위처럼 쓰면 A와 B가 직접 호출되기 때문에 Python의 MRO(Method Resolution Order)를 무시하게 된다.

MRO(Method Resolution Order) ?
파이썬에서 상속받은 클래스들 중 어떤 순서로 메서드를 탐색할지 정의하는 규칙

👉 super()는 MRO를 따라 호출되기 때문에 다중 상속에서 예상 가능한 순서로 부모 메소드가 실행됨!

  1. 클래스명이 하드코딩됨

    나중에 상속 구조가 바뀌거나 리팩토링할 때 변경 포인트가 많아져서 유지보수가 어려움

  2. 동적 상속이 안 됨

class DynamicChild(some_parent_class):
    def __init__(self):
        some_parent_class.__init__(self)  # ← 이거 위험!

이렇게 되면 some_parent_class가 뭔지 미리 모르면 안 되고,
런타임에서 동적으로 상속하거나 테스트용 mock class 쓸 때 깨질 수 있음!

📌 실습 예제

# 중간고사 클래스와 기말고사 클래스를 상속관계로 만들고 각각의 점수를 초기화하자.
# 또한, 총점 및 평균을 반환하는 기능도 만들어보자

class MidExam:
    def __init__(self, m_num1, m_num2):
        print('[cMidExam] __init__()')

        self.mid_eng_score = m_num1
        self.mid_math_score = m_num2

    def printscore(self):
        print(f'english : {self.mid_eng_score}, math : {self.mid_math_score}')

class FinalExam(MidExam):
    def __init__(self, m_num1, m_num2, m_num3, m_num4):
        print('[cFinalExam] __init__()')

        super().__init__(m_num1, m_num2)

        self.final_eng_score = m_num3
        self.final_math_score = m_num4

    def printscore(self):
        super().printscore()
        print(f'english : {self.final_eng_score}, math : {self.final_math_score}')

    def gettotalscore(self):
        total = self.final_eng_score + self.final_math_score
        total += self.final_eng_score + self.final_math_score

        return total

    def getavaragescore(self):
        avg = self.gettotalscore() / 4
        return avg

exam = FinalExam(10,20,30,80)
exam.printscore()

print(f'Total score: {exam.gettotalscore()}')
print(f'Average score: {exam.getavaragescore()}')

<결과>
[cFinalExam] __init__()
[cMidExam] __init__()
english : 10, math : 20
english : 30, math : 80
Total score: 220
Average score: 55.0

✅ 회고

  • __init__()은 상속받은 부모 클래스에서도 명시적으로 호출해야 작동함
  • 다중 상속 또는 유연한 구조를 만들려면 직접호출보다는 super() 사용이 필수!
  • super()는 MRO를 따르며, 유지보수 및 테스트 환경에서도 안정적이기 때문에
profile
Hello. I'm jimin:)

0개의 댓글