Developer/Python

python : 상속 (inheritance)

단님 2025. 3. 25. 22:33
728x90
상속

객체 지향 언어의 큰 특징으로 부모 클래스가 가진 모든 속성을 자식에게 물려주는 것을 의미한다.
동일한 코드가 반복되지 않고 공통된 속성을 부모 클래스에서 관리하여 유지보수성을 높일 수 있다.

형식
class 부모클래스:
    def __init__(self):
        print("부모 클래스 생성자")

    def 부모메서드(self):
        print("부모 메서드 호출")

class 자식클래스(부모클래스):
    def __init__(self):
        super().__init__()  # 부모 클래스 생성자 호출
        print("자식 클래스 생성자")

    def 자식메서드(self):
        print("자식 메서드 호출")

super()

super() 은 부모 클래스의 메서드나 생성자를 호출하기 위해 사용된다.
super()를 사용하지 않고 부모클래스를 부모클래스.부모메서드로 접근할 수 있지만, 이는 유지 보수 측면에서 super()를 사용하는 것이 더 유연하다.
특히 다중 상속에서 MRO(Method Resolution Order)를 통해 안정적인 부모 메서드 호출이 가능하다.

class 부모:
    def __init__(self):
        print("부모 생성자")

    def say_hello(self):
        print("부모: 안녕하세요")

class 자식(부모):
    def __init__(self):
        # 부모 클래스의 __init__을 호출
        super().__init__()
        print("자식 생성자")

    def say_hello(self):
        # 부모 클래스의 say_hello를 호출
        super().say_hello()
        print("자식: 안녕!")

다중 상속

파이썬은 다중 상속을 지원한다.
2개 이상의 부모클래스를 상속하는 것을 의미하고, 모든 속성을 그대로 상속하고, 메서드 오버라이딩도 가능하다.
메서드 결정 순서는 클래스 속성이 __mro__에서 정의한다 (Method Resolution Order)
python 3 부터는 모든 클래스의 선언부를 명시하지 않아도 object 클래스를 묵시적으로 상속한다.
즉 파이썬은 모든 객체가 만들어질 때 object 객체가 기본적으로 만들어졌다고 보면 된다.

class A:
    def greeting(self):
        print("A: 안녕")

class B:
    def greeting(self):
        print("B: 헬로우")

class C(A, B):  # A, B 둘 다 상속
    pass

c = C()
c.greeting()
# 결과 
# A : 안녕

메서드 결정 짓는 순서를 보고 싶다면, 해당 자식클래스.__mro__이나 해당 자식클래스.mro()으로 출력해서 볼 수 있다.
부모클래스를 클래스의 파라미터형식으로 표기가 되는데 , mro는 앞에서 뒤로 진행되는 것을 볼 수 있다.

- 다이야몬드형 다중상속

class A:
    def show(self):
        print("A")

class B(A):
    def show(self):
        print("B")
        super().show()

class C(A):
    def show(self):
        print("C")
        super().show()

class D(B, C):  # 다중 상속
    def show(self):
        print("D")
        super().show()

d = D()
d.show()

이에 대한 결과는 어떻게 될까 ? 한 번 생각해봐도 좋을 것 같다.
결과는 다음과 같다.

D
B
C
A

이 결과는 __mro__를 찍어보면 확인해 볼 수 있다. 실행되는 순서가 다음과 같다.

print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)


자바에서는 다중 상속을 사용했던가 잠깐 생각해보게 된다.
생각해보면 사용하지 않은 것 같다. 계층적으로 확장해나갔지, 하위계층에서 여러개의 상위계층을 포함한 적이 있는가 생각해보게 된다.

class A {
    void hello() { System.out.println("A"); }
}

class B extends A {
    void hello() { System.out.println("B"); }
}

class C extends A {
    void hello() { System.out.println("C"); }
}

// 자바에서는 아래처럼 B, C를 동시에 상속할 수 없음
class D extends B, C {  // 컴파일 오류!
    // 어떤 hello()를 쓸지 모호함
}

그렇다. 자바에서는 다중 상속을 지원하지 않고, 다중 구현은 지원하였다.
다이야몬드형 상속상 super 키워드의 문제가 발생하게 되기 때문이다.

interface A {
    default void hello() { System.out.println("A"); }
}

interface B {
    default void hello() { System.out.println("B"); }
}

class C implements A, B {
    // 둘 다 hello() 가지고 있으니 오버라이딩 필요
    public void hello() {
        A.super.hello();  // 명시적으로 선택
    }
}

다중 인터페이스를 구현하게 되면 명시적으로 어떤 인터페이스를 사용할지 명시를 해줘야 한다.

이쯤 다시 궁금해진다.
그렇다면 오버라이딩이 super()를 통해 된다면,
오버 로딩은 ?


파이썬의 오버로딩(overloading)

파이썬은 기본적으로 오버로딩을 지원하지 않는다.
똑같은 이름으로 두번 정의하면 마지막이 덮어씌워진다.
클래스에 __init__(self,name) , __init__(self) 를 정의하면, 경고등이 뜨면서
클래스 생성시에 인자 두개를 넣으면 마지막인 __init__(self)가 발동되면서
__init__() takes 1 positional argument but 2 were given 이라는 오류가 발생하게 된다.

아래에 다른 예시를 확인해보자.

def greet(name):
    print("Hello", name)

def greet(name, age):
    print(f"Hello {name}, you are {age} years old.")

greet("길동")  
# TypeError! → 마지막에 정의된 greet()만 살아남고, 이전 greet는 덮어써짐


그렇다면 어떻게 오버로딩을 구현할까 ?
생각보다 간단한 문제였다.
파이썬의 디폴트 파라미터 또는 가변인자를 활용할 수 있다.

디폴트 파라미터 활용
def greet(name, age=None):
    if age:
        print(f"{name}, {age}살 반가워~")
    else:
        print(f"{name}, 안녕~")

greet("길동")          # 길동, 안녕~
greet("길동", 30)      # 길동, 30살 반가워~
*arg 활용
def greet(*args):
    if len(args) == 1:
        print(f"{args[0]}, 안녕~")
    elif len(args) == 2:
        print(f"{args[0]}, {args[1]}살 반가워~")
    else :
        print("이름 없음")

greet()
greet("길동")
greet("길동", 30)
*arg,**kwargs 활용
def greet(*args, **kwargs):
    # 위치 인자가 1개만 들어오면: 이름만
    if len(args) == 1 and not kwargs:
        name = args[0]
        print(f"안녕 {name}")

    # 위치 인자가 2개면: 이름, 나이
    elif len(args) == 2:
        name, age = args
        print(f"안녕 {name}, 너는 {age}살이구나")

    # 키워드 인자로만 받았을 때
    elif "name" in kwargs and "age" in kwargs:
        print(f"안녕 {kwargs['name']}, 너는 {kwargs['age']}살이구나")

    # 그 외는 에러 처리
    else:
        print("지원하지 않는 인자입니다")

# 호출 예시
greet("길동")
greet("길동", 30)
greet(name="길동", age=30)
greet(age=30)  # 지원하지 않는 인자입니다


다른 구현 형식으로는 데코레이터가 있다고 하지만 아직 공부하기 전이니 추후에 기억해서 만들어보자.

'Developer > Python' 카테고리의 다른 글

python : 할당과 복사  (0) 2025.03.26
python : Iterator, Generator  (0) 2025.03.26
python : 자료형 . 기본 자료형 및 내장 자료형  (0) 2025.03.20
python : 변수  (0) 2025.03.19