-
83, 84 - (파이썬) 오버라이드와 super() 함수 / 상속과 컴포지션study with Q - 파이썬 2024. 9. 9. 19:26
83강
# 오버라이드 (oevrride): 재정의
: 부모에 정의되어 있는 함수를 자식에서 다시 정의하는 것
#오버라이드 class 부모: def 함수(self): print("부모의 함수") class 자식(부모): def 함수(self): print("자식의 함수") child = 자식() child.함수() >>> 자식의 함수
파이썬에서는 먼저 자신의 클래스에 그 함수가 있는지를 확인하고 없다면 부모 클래스에 그 함수가 있는지 찾는다.
# 자식 함수에서 부모 함수 호출하기
class 부모: def 함수(self): print("부모의 함수") class 자식(부모): def 함수(self): super().함수() print("자식의 함수") super().함수() child = 자식() child.함수() >>> 부모의 함수 자식의 함수 부모의 함수
# 간단한 사용의 예
- before
더보기class 빨간버튼: def __init__(self): print("버튼 초기화") print("버튼 만들기") print("버튼 출력") print("버튼 색상 변경 - 빨간색") class 파란버튼: def __init__(self): print("버튼 초기화") print("버튼 만들기") print("버튼 출력") print("버튼 색상 변경 - 파란색") class 초록버튼: def __init__(self): print("버튼 초기화") print("버튼 만들기") print("버튼 출력") print("버튼 색상 변경 - 초록색") 빨간버튼() print() 파란버튼() print() 초록버튼() >>> 버튼 초기화 버튼 만들기 버튼 출력 버튼 색상 변경 - 빨간색 버튼 초기화 버튼 만들기 버튼 출력 버튼 색상 변경 - 파란색 버튼 초기화 버튼 만들기 버튼 출력 버튼 색상 변경 - 초록색
- after
class 버튼: def __init__(self): print("버튼 초기화") print("버튼 만들기") print("버튼 출력") class 빨간버튼(버튼): def __init__(self): super().__init__() print("버튼 색상 변경 - 빨간색") class 파란버튼(버튼): def __init__(self): super().__init__() print("버튼 색상 변경 - 빨간색") class 초록버튼(버튼): def __init__(self): super().__init__() print("버튼 색상 변경 - 초록색") 빨간버튼() print() 파란버튼() print() 초록버튼() >>> 버튼 초기화 버튼 만들기 버튼 출력 버튼 색상 변경 - 빨간색 버튼 초기화 버튼 만들기 버튼 출력 버튼 색상 변경 - 빨간색 버튼 초기화 버튼 만들기 버튼 출력 버튼 색상 변경 - 초록색
84강
※ 오늘 강의에 내용에 나오는 현대적인 개발 트랜드에 맞지 않는 코드로 공부를 위해 만든 코드임을 참고해주세요.
#상속과 컴포지션
기억해라 용어용어
더보기1) 상속
: 다른 누군가가 만들어 놓은 기본 형태에 내가 원하는 것만 추가하거나 교체하는 것
2) 부모
: 기반이 되는 것
3)자식
: 이를 기반으로 생성한 것
#활용 예 1) 부모의 모든 것 가져오기
class Student: def __init__(self, 수학): self.수학 = 수학 class StudentList(list): pass 학생목록 = StudentList() 학생목록.append(Student(100)) 학생목록.append(Student(80)) 학생목록.append(Student(70)) for 학생 in 학생목록: print(학생.수학) >>> 100 80 70
더보기# StudentList는 리스트이기 때문에 리스트 클래스를 상속 받기만 하면 리스트 클래스에 있는 모든 기능들을 가져올 수 있다.
# 그럼 StudentList를 만들었을 때, 그 리스트 뒤에 점을 찍는 것 만으로 리스트가 갖고 있는 모든 기능들을 활용할 수 있다.
# append라는 기능으로 학생을 몇 명 추가하고 for 반복문으로 학생 목록을 돌린다. (리스트이므로 사용 가능)
# for 반복문 코드를 이용해서 학생을 하나하나 출력도 가능하다.#예외 출력
class Student: def __init__(self, 수학): self.수학 = 수학 class StudentList(list): def append(self, 요소): if not isinstance(요소, Student): raise TypeError("Student를 전달해주세요.") super().append(요소) 학생목록 = StudentList() 학생목록.append(100) for 학생 in 학생목록: print(학생.수학) >>> Traceback (most recent call last): File "/home/runner/number/main.py", line 13, in <module> 학생목록.append(100) File "/home/runner/number/main.py", line 8, in append raise TypeError("Student를 전달해주세요.") TypeError: Student를 전달해주세요.
더보기# 그런데 이렇게 리스트의 append를 활용하는 것 만으로는 Student 인스턴스가 아닌 다른 인스턴스도 매개변수로 들어 올 수 있다.
# 그래서 append를 재정의-오버라이드- 한다.
# append라는 이름의 함수를 만들고 만약 요소의 타입이 Student가 아니라면 Student를 전달해달라고 예외를 발생시켜 버린다.
# append와 관련된 코드를 추가하기 위해 부모의 append를 호출해서 요소를 전달하면 부모의 기능을 모두 활용하면서도 Student 객체만 받는 append 함수가 만들어진다.#근데 계속 에러가 다른 곳에서 발생하길래 챗GPT에게 물어봐서
if not isinstance(요소, Student): raise TypeError("Student를 전달해주세요.")
여기 코드를 좀 바꿈
#활용 예 2) 추가 정의 - 자식의 추가적인 함수를 만들기
class Student: def __init__(self, 수학): self.수학 = 수학 class StudentList(list): def append(self, 요소): if not isinstance(요소, Student): raise TypeError("Student를 전달해주세요.") super().append(요소) def sum(self): output = 0 for 학생 in self: output += 학생.수학 return output def average(self): return self.sum() / len(self) 학생목록 = StudentList() 학생목록.append(Student(100)) 학생목록.append(Student(80)) print(학생목록.sum()) print(학생목록.average()) >>> 180 90.0
더보기# 학생의 모든 점수를 더하는 썸이라는 이름의 함수와 에버러지라는 이름의 함수를 구현해보자.
# StudentList 안에다가 썸을 구현해주면 셀프가 리스트이기 때문에 리스트를 반복을 돌린다.
# 내부에서 아웃풋이라는 변수를 하나 선언허고 아웃풋 플러스는 학생의 수학점수로 더한 다음 리턴을 해주면 코드가 구현됨.
# 평균을 구하는 에버러지 함수는 self.sum으로 모두 더하고 셀프의 길이를 구한 다음 나눠서 리턴을 해주면 코드가 구현된다.(self는 StudentList 객체를 나타내며, len(self)는 이 객체에 포함된 요소들의 수를 반환한다. 예를 들어, StudentList 객체에 5개의 Student 객체가 있다면, len(self)는 5를 반환한다.)
# 상속으로 발생할 수 있는 위험한 상황
: (현대적인 개발 트랜드에서는 위와 같은 코드를 사용하지 못하는 이유) 리스트가 너무 강력해서 코드가 붕괴될 수 있다.
학생목록 = StudentList() 학생목록.append(Student(100)) 학생목록.append(Student(80)) 학생목록[0] = 0 # 첫번째 요소를 0으로 바꿔버리는 것만으로도 오류를 발생시킬 수 있다. print(학생목록.sum()) print(학생목록.average())
🧐 오버라이드로 안전하게 막으면 안되나? → 막을 것이 너무 많아진다.
따라서 '많은 요소를 갖고 있는 클래스'를 상속받아서 위험요소를 숨기는 것[aka 블랙리스트]보다는 '많은 요소를 갖고 있는 클래스'를 캡슐화로 숨기고 필요한 요소만 공개하는 것[aka 화이트리스트]가 쉽다.
#컴포지션
: 클래스를 상속 받는 것이 아니라 내부에 캡슐화에서 포함해서 활용하여 코드를 구현하는 것
: 프레임워크에서 강제하지 않을 때 사용한다.
class Student: def __init__(self, 수학): self.수학 = 수학 class StudentList(list): def __init__(self): #외부로부터 캡슐화 해서 숨기기 self.__리스트 = [] def append(self, 요소): if not isinstance(요소, Student): raise TypeError("Student를 전달해주세요.") self.__리스트.append(요소) #self.__리스트로 수정 def sum(self): output = 0 for 학생 in self.__리스트: #self.__리스트로 수정 output += 학생.수학 return output def average(self): return self.sum() / len(self.__리스트) #self.__리스트로 수정 학생목록 = StudentList() 학생목록.append(Student(100)) 학생목록.append(Student(80)) print(학생목록.sum()) print(학생목록.average()) >>> 180 90.0
더보기#처음에 작성했던 코드처럼 리스트라는 것을 외부로부터 캡슐화해서 숨기고 이걸 기반으로 코드를 구현하게 작성한다.
#그렇게 하면 이전만큼 StudentList가 강력한 기능을 갖지는 않지만 훨씬 안전해진다.# insert, remove, sort함수 등등 추가적으로 하나하나 구현해야 하는 것이 아니고 필요한 것만 추가하기 때문에 안전할 수 있고
# 상속해도 안전하게 오버라이드 해야하기 때문에 하나하나 구현하는 것은 똑같다.
# 그럼 상속은 언제 씀?
: 프레임워크가 상속을 강제할 때
'study with Q - 파이썬' 카테고리의 다른 글
87 - (파이썬) 모듈을 읽는 방법 (0) 2024.09.13 85 - (파이썬) 스택과 큐 (2) 2024.09.09 81 - 83 (파이썬) 강의 정리 포기, 코드 복붙 (1) 2024.09.01 80 - (파이썬) 클래스 문법 기본 (4) 2024.08.27 79 - (파이썬) 클래스 등장 이전 (0) 2024.08.26