-
TIL.34 클래스 상속(오버라이딩 및 추상클래스)TIL 2020. 11. 11. 15:37728x90
## 클래스상속하기_2
## 메서드 오버라이딩(method overriding)
## 메서드 오버라이딩 == 파생 클래스 안에서 기반 클래스의 메서드를 새로 정의하는 방법이다.
class Person: def greeting(self): print('안녕하세요.') class Student(Person): def greeting(self): print('안녕하세요. 저는 파이썬 코딩 도장 학생입니다.') james = Student() james.greeting() # '안녕하세요. 저는 파이썬 코딩 도장 학생입니다.'
# 안녕하세요가 아닌 안녕하세요. 저는 파이썬 코딩 도장 학생입니다가 출력되었다.
## 오버라이딩(overriding)은 무시하다, 우선하다라는 뜻을 가지고 있는데
말 그대로 기반 클래스의 메서드를 무시하고 새로운 메서드를 만든다는 뜻이다.
## 여기서는 Person 클래스의 greeting 메서드를 무시하고 Student 클래스에서 새로운 greeting 메서드를 만들었다.
## 별도의 코드는 없으며 위와 같이 작성하는 방법 자체가 오버라이딩이다.
## 그럼 메서드 오버라이딩은 왜 사용할까?## 보통 프로그램에서 어떤 기능이 같은 메서드 이름으로 계속 사용되어야 할 때 메서드 오버라이딩을 활용하는데
## 만약 Student 클래스에서 인사하는 메서드를 greeting2로 만들어야 한다면
## 모든 소스 코드에서 메서드 호출 부분을 greeting2로 수정해야하는 번거로움이 있다.
## 다시 위의 코드를 본다면, 각 클래스에서 안녕하세요가 반복되는 것을 알 수 있는데
## 위와 같이 오버라이딩된 메서드에서 super()로 기반 클래스의 메서드를 호출해보자.
class Person: def greeting(self): print('안녕하세요.') class Student(Person): def greeting(self): super().greeting() # 기반 클래스의 메서드 호출하여 중복을 줄임 print('저는 파이썬 코딩 도장 학생입니다.') james = Student() james.greeting() # 안녕하세요 저는 파이썬 코딩 도장 학생입니다.
### 즉, 중복되는 기능은 파생 클래스에서 다시 만들지 않고, 기반 클래스의 기능을 사용하면 된다.
### 이처럼 메서드 오버라이딩은 원래 기능을 유지하면서 새로운 기능을 덧붙일 때 사용한다.
## 다중 상속 사용하기
## 다중 상속이란 아래와 같이 여러 기반 클래스로부터 상속받아서 파생 클래스를 만드는 방법이다.
class 기반클래스이름1: 코드 class 기반클래스이름2: 코드 class 파생클래스이름(기반클래스이름1, 기반클래스이름2): 코드
## 다중 상속 예를 들어보자
# 사람, 대학교, 대학생 여기서는 대학생이 사람과 대학교의 다중 상속된 파생클래스이다.
class Person: def greeting(self): print('안녕하세요.') class University: def manage_credit(self): print('학점 관리') class Undergraduate(Person, University): def study(self): print('공부하기') james = Undergraduate() james.greeting() # 안녕하세요.: 기반 클래스 Person의 메서드 호출 james.manage_credit() # 학점 관리: 기반 클래스 University의 메서드 호출 james.study() # 공부하기: 파생 클래스 Undergraduate에 추가한 study 메서드 # 안녕하세요. 학점 관리 공부하기
## 먼저 기반 클래스 Person 과 기반 클래스 University 를 ,(콤마)를 구분으로 해서 작성한다.
## 이렇게 하면 두 기반 클래스의 기능을 모두 상속 받게 된다.
## 다이아몬드 상속 ##
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 x = D() x.greeting() :: 안녕하세요. B입니다.
## 여기서 D는 B와 C 두 기반 클래스의 파생클래스로 B,C 로 입력하여 우선순위가 B가 먼저이기에 B의 값이 출력된다.
# 파이썬은 다중 상속을 한다면 class D(B, C):의 클래스 목록 중 왼쪽에서 오른쪽 순서로 메서드를 찾는다.
# 그러므로 같은 메서드가 있다면 B가 우선합니다.
만약 상속 관계가 복잡하게 얽혀 있다면 MRO를 살펴보는 것이 편리하다.
## 이는 메서드 탐색 순서를 확인하면 알 수 있다.
## 메서드 탐색 순서 확인하기
## 많은 프로그래밍 언어들이 다이아몬드 상속에 대한 해결책을 제시하고 있는데
파이썬에서는 메서드 탐색 순서(Method Resolution Order, MRO)를 따른다.
## 다음과 같이 클래스 D에 메서드 mro를 사용해보면 메서드 탐색 순서가 나옵니다(클래스.__mro__ 형식도 같은 내용)
# 클래스.mro()
D.mro() # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
# 클래스 간의 관계가 다이아몬드 모양을 닯았다고 하여
객체지향 프로그래밍에서는 이런 상속 관계를 다이아몬드 상속이라 부른다.
## 결론부터 말하자면 프로그래밍에서는 이렇게 명확하지 않고 애매한 상태를 피해야야만 한다. ##
## object 클래스
## 파이썬에서는 object 클래스는 모든 클래스의 조상이다.
# int의 MRO를 출력해보면 int 자기 자신과 object가 출력됩니다.
print(int.mro()) :: [<class 'int'>, <class 'object'>]
# 다이 아몬드 상속에 대한 의견_코딩도장여기서는 클래스 A를 상속받아서 B, C를 만들고, 클래스 B와 C를 상속받아서 D를 만들었습니다.
그리고 A, B, C 모두 greeting이라는 같은 메서드를 가지고 있다면 D는 어떤 클래스의 메서드를 호출해야 할까요? 조금 애매합니다.
프로그래밍에서는 이렇게 명확하지 않고 애매한 상태를 좋아하지 않습니다.
프로그램이 어떨 때는 A의 메서드를 호출하고, 또 어떨 때는 B 또는 C의 메서드를 호출한다면 큰 문제가 생깁니다.
만약 이런 프로그램이 우주선 발사에 쓰인다면 정말 끔찍합니다.
그래서 다이아몬드 상속은 문제가 많다고 해서 "죽음의 다이아몬드"라고도 부릅니다.
## 추상 클래스 사용하기
## 추상 클래스는 메서드의 목록"만" 가진 클래스이며 상속받는 클래스에서 메서드 구현을 강제하기 위해 사용한다.
# 파이썬에서는 추상 클래스라는 기능을 제공하는데 abc 모듈을 불러옴과 동시에 아래의 형식으로 만들 수 있다.
# ( abc는 abstract base class의 약자이다)
# 만약 import abc로 모듈을 가져왔다면 abc.ABCMeta, @abc.abstractmethod로 사용해야 한다.
from abc import * class 추상클래스이름(metaclass=ABCMeta): @abstractmethod def 메서드이름(self): 코드
## 추상클래스를 만들고 이를 상속하는 학생 클래스르 만들어보자.
from abc import * class StudentBase(metaclass=ABCMeta): @abstractmethod def study(self): pass @abstractmethod def go_to_school(self): pass class Student(StudentBase): def study(self): print('공부하기') james = Student() ## error james.study() # Traceback (most recent call last): File "/Users/munseunghui/playground/python_Study/36.6추상클래스 사용하기.py", line 31, in <module> james = Student() TypeError: Can't instantiate abstract class Student with abstract methods go_to_school
## 이를 실행시켜보면 Student 메서드에 james 라는 인스턴스를 만드는 순간부터 error가 남을 알 수 있다.
## 에러의 이유는 아래와 같다.
## 먼저 StudentBase라는 추상클래스에서는 study와 go_to_school 두 가지 메서드를 정의하였다.
## 하지만 추상클래스를 상속받은 Student 클래스에서는 study만을 정의하고 있어 에러가 난것이다.
## 따라서 추상 클래스를 상속받았다면 @abstractmethod가 붙은 추상 메서드를 "모두" 구현해야 한다.
## 다음과 같이 Student에서 go_to_school 메서드도 구현해줍니다.
from abc import * class StudentBase(metaclass=ABCMeta): @abstractmethod def study(self): pass @abstractmethod def go_to_school(self): pass class Student(StudentBase): def study(self): print('공부하기') def go_to_school(self): print('학교가기') james = Student() ## 추상클래스의 추상 메서드를 모두 구현했는지 확인되는 시점 james.study() # 공부하기 james.go_to_school() # 학교가기
### 이처럼 추상클래스는 파생클래스가 반드시 구현해야하는 메서드를 정해줄 수 있다.
## 참고로 추상 클래스의 추상 메서드를 모두 구현했는지 확인하는 시점은 파생 클래스가 인스턴스를 만들 때이다.
# 따라서 james = Student()에서 확인합니다(구현하지 않았다면 TypeError 발생).
### 추상 클래스를 빈 메서드로 만들어야만 하는 이유
## 추상 클래스는 인스턴스로 만들 수 없다 --> 빈 메서드로 만들어야하는 가장 큰 이유이다 ##
## 따라서 추상 클래스의 추상 메서드를 만들때 pass만 넣어서 빈 메서드로 만든것이다.
## 왜냐하면 추상 클래스는 인스턴스를 만들 수 없으니, 추상 메서드로 호출 할 일 이 없기 때문이다.##
@abstractmethod def study(self): pass # 추상 메서드는 호출할 일이 없으므로 빈 메서드로 만듦 @abstractmethod def go_to_school(self): pass # 추상 메서드는 호출할 일이 없으므로 빈 메서드로 만듦
## 아래와 같이 추상클래스로 인스턴스를 생성하면 error가 발생한다.
james = StudentBase() Traceback (most recent call last): File "<pyshell#3>", line 1, in <module> james = StudentBase() TypeError: Can't instantiate abstract class StudentBase with abstract methods go_to_school, study
## 즉, 추상 클래스는 인스턴스로 만들 때 사용하지 않으며 오로지 상속에만 사용한다.
## 그리고 파생 클래스에서 반드시 구현해야 하는 메서드를 정해 줄 때 사용한다.
# 지금까지 상속에 대해 알아보았는데 내용이 다소 어려웠습니다.
## 여기서는 클래스를 상속받는 방법과 메서드 오버라이딩 방법 정도만 기억하면 됩니다.
## 그리고 상속은 같은 종류이면서 동등한 기능일 때 사용한다는 점이 중요합니다.
# 다중 상속과 추상 클래스는 나중에 필요할 때 다시 돌아와서 찾아보자.
## 오버라이딩을 사용했다고 해서 기반 클래스의 메서드를 호출 할 수 없는 것은 아니다.
728x90'TIL' 카테고리의 다른 글
TIL.36 selenium을 이용한 이미지 크롤링 자동화 (2) 2020.11.13 TIL.35 두점 사이의 거리 구하기 (0) 2020.11.12 TIL.33 클래스 상속(기반 클래스 및 파생클래스) (0) 2020.11.10 TIL.32 클래스의 속성과 메서드 (0) 2020.11.09 TIL. 31 클래스의 속성, 비공개 속성 사용하기 (0) 2020.11.08