ABOUT ME

일단 좀 해봅시다

Today
Yesterday
Total
  • 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함수 등등 추가적으로 하나하나 구현해야 하는 것이 아니고 필요한 것만 추가하기 때문에 안전할 수 있고

    # 상속해도 안전하게 오버라이드 해야하기 때문에 하나하나 구현하는 것은 똑같다.

    # 그럼 상속은 언제 씀?

    : 프레임워크가 상속을 강제할 때

Designed by Tistory.