[Python] 상속(Inheritance)과 오버라이딩(Overriding)

cdwde·2021년 5월 22일
0

🎈 단일 상속 (Inheritance)

class 부모클래스:
    ...내용...

class 자식클래스(부모클래스):
   ...내용...
  • 부모클래스: 상속하는 클래스
  • 자식클래스: 상속받는 클래스
  • 자식 클래스가 부모 클래스의 내용 (속성, 메소드) 쓸 수 있음

예시

class Country:
    name = '국가명'
    population = '인구'
    capital = '수도'

    def show(self):
        print('국가 클래스의 메소드')

class Korea(Country):
    def __init__(self, name):
        self.name = name
    
    def show_name(self):
        print('국가 이름은', self.name)

a = Korea('대한민국')
a.show()
a.show_name()
print(a.capital)
print(a.name)

#출력 결과
#국가 클래스의 메소드
#국가 이름은 대한민국
#수도
#대한민국

🎈 다중 상속 (Multiple inheritance)

class 부모클래스1:
    ...내용...
    
class 부모클래스2:
    ...내용...
    
class 자식클래스(부모클래스1, 부모클래스2):
    ...내용...
  • C#, JAVA는 다중상속 불가능
  • 파이썬, C++은 다중상속 가능
  • 상속 개수에 제한 없음

예시

class ParentOne:
    def func(self):
        print("ParentOne 함수 호출")

class ParentTwo:
    def func(self):
        print("ParentTwo 함수 호출")

class Child(ParentOne, ParentTwo):
    def childFunc(self):
        ParentOne.func(self)
        ParentTwo.func(self)

obj = Child()
obj.childFunc()
obj.func()		#상속받을 클래스의 이름을 나열한 순서대로 이름을 찾기 때문에 ParentOne의 func() 호출

#출력 결과
#ParentOne 함수 호출
#ParentTwo 함수 호출
#ParentOne 함수 호출

❗ 다중 상속 주의할 점

다이아몬드 상속에서 문제 발생할 수 있음

예시

  • A, B, C 모두 greeting이라는 같은 메소드를 갖고 있을 때 D는 어떤 클래스의 메서드를 호출?
class A:
    def greeting(self):
        print("A입니다")

class B(A):
    def greeting(self):
        print("B입니다")

class C(A):
    def greeting(self):
        print("C입니다")

class D(B, C):
    pass

obj = D()
obj.greeting()

#출력 결과
#B입니다

🎈 메소드 탐색 순서 확인하기

파이썬은 메서드 탐색 순서 (Method Resolution Order, MRO)를 따름

  • 다중 상속 시 상속 클래스 목록 중 왼쪽에서 오른쪽 순서로 메소드를 찾음

  • 상속 관계가 복잡하다면 MRO 살펴보기

  • 클래스.__mro__, 클래스.mro()로 메소드 탐색 순서 확인 가능

class A:
    def greeting(self):
        print("A입니다")

class B(A):
    def greeting(self):
        print("B입니다")

class C(A):
    def greeting(self):
        print("C입니다")

class D(B, C):
    pass

print(D.mro())

obj = D()
obj.greeting()

#출력 결과
#[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
#B입니다
  • MRO에 따르면 D의 메소드 호출 순서는 자기 자신 D, 그다음 B

  • 따라서 D로 인스턴스를 만들고 greeting을 호출하면 B의 greeting 호출(D는 greeting 메소드 없기 때문)


🎈 메소드 오버라이딩 (Method overriding)

일반적인 메소드 오버라이딩

부모 클래스의 메소드를 자식 클래스에서 재정의

class Country:
    name = '국가명'
    population = '인구'
    capital = '수도'

    def show(self):
        print('국가 클래스의 메소드')

class Korea(Country):
    def __init__(self, name, population, capital):
        self.name = name
        self.population =population
        self.capital = capital
    
    def show(self):
        print('이름 {}, 인구 {}, 수도 {} '.format(self.name, self.population, self.capital))

a = Korea('대한민국', 50000000, '서울')
a.show()	#부모 클래스의 show()메소드 무시되고 자식클래스의 show()메소드 수행

#출력 결과
#이름 대한민국, 인구 50000000, 수도 서울

super(): 부모 메소드 호출하기

  • super(): 자식클래스 내에서 부모클래스 내용 사용하고 싶은 경우
  • 부모클래스의 메소드도 수행하고 자식클래스의 메소드도 수행

예시 1

class Country:
    name = '국가명'
    population = '인구'
    capital = '수도'

    def show(self):
        print('국가 클래스의 메소드')

class Korea(Country):
    def __init__(self, name, population, capital):
        self.name = name
        self.population =population
        self.capital = capital
    
    def show(self):
        super().show()
        print('이름 {}, 인구 {}, 수도 {} '.format(self.name, self.population, self.capital))

a = Korea('대한민국', 50000000, '서울')
a.show()	#부모 클래스의 show()메소드 무시되고 자식클래스의 show()메소드 수행

#출력 결과
#국가 클래스의 메소드
#이름 대한민국, 인구 50000000, 수도 서울

예시 2

  • super().__init__() 와 같이 기반 클래스 Person의 __init__ 메서드를 호출해주면 기반 클래스가 초기화되면서 속성 만들어짐
class Person:
    def __init__(self):
        print('Person __init__')
        self.hello = '안녕하세요'

class Student(Person):
    def __init__(self):
        print('Student __init__')
        super().__init__()  #super()로 기반 클래스의 __init__ 메소드 호출
        self.school = '학교'

human = Student()
print(human.school)
print(human.hello)

#출력 결과
#Student __init__
#Person __init__
#학교
#안녕하세요

* 기반클래스를 초기화하지 않아도 되는 경우

  • 파생 클래스에서 __init__ 메서드를 생략한다면 기반 클래스의 __init__이 자동으로 호출되므로 super() 사용하지 않아도 됨
class Person:
    def __init__(self):
        print('Person __init__')
        self.hello = "안녕"

class Student(Person):
    pass

human = Student()
print(human.hello)

#출력 결과
#Person __init__
#안녕
  • super는 파생클래스와 self를 넣어 현재 클래스가 어떤 클래스인지 명확하게 표시하는 방법도 있음
  • super(파생클래스, self).메소드
class Person:
    def __init__(self):
        print('Person __init__')
        self.hello = "안녕"

class Student(Person):
    def __init__(self):
        print('Student __init__')
        super(Student, self).__init__()

❗ 예외 만들기

  • 사용자가 직접 예외처리하면 코드의 직관성을 높일 수 있음
  • 파일 하나를 만들어 예외를 정의
  • Exception 클래스를 상속
#my_exception_list.py
class UnexpectedError(Exception):	#Exception 클래스 상속
    def __init__(self):
        super().__init__('가위 바위 보 중 입력해주세요')
    
#test.py
from my_exception_list import UnexpectedError

value = input('가위, 바위, 보 중 하나 선택: ')

try:
  if value not in ['가위', '바위', '보']:
    raise UnexpectedError
except UnexpectedError as err:
    print('입력값이 올바르지 않습니다', err)

참고
https://wikidocs.net/16073
https://wayhome25.github.io/python/2017/02/26/py-16-inheritance/
https://blog.hexabrain.net/286
https://dojang.io/mod/page/view.php?id=2388
https://dojang.io/mod/page/view.php?id=2386

0개의 댓글