Method Overriding

EBAB!·2024년 1월 21일
0

Method Overriding

자식 클래스가 부모 클래스로부터 상속받은 메소드를 재정의(override)하는 것을 의미한다.
이 때 자식 클래스는 상속받은 메소드와 동일한 이름, 매개변수를 가지지만, 구체적인 실행 내용은 변경할 수 있다.

Method Overriding의 목적

  • 확장성 (Extensibility): 자식 클래스는 부모 클래스의 기능을 확장하거나 수정할 수 있기 떄문에 코드 재사용성과 유지보수가 용이하다.
  • 다형성 (Polymorphism): 같은 인터페이스나 클래스 계층 구조에 속하는 객체들이 다양한 방식으로 작동할 수 있다.

특징

  • 메소드 시그니쳐: 오버라이딩 할 메소드는 부모 클래스의 메소드와 동일한 이름, 매개변수 리스트를 가져야 한다.
  • 접근 제어: 오버라이딩된 메소드는 부모 클래스의 메소드보다 접근성이 더 제한적이면 안된다.
  • 리턴 타입: 자식 클래스의 메소드가 부모 클래스의 메소드와 동일하거나 더 구체적인 리턴 타입을 가져야 한다.
  • super 키워드: 자식 클래스에서는 super키워드를 사용해서 부모 클래스의 메소드를 호출할 수 있다.

상속 방식

부모 클래스로부터 상속을 받을 때 인스턴스화가 되는 시점에 부모로부터 상속을 받는다.

class ParentEx1():
    def __init__(self):
        self.value = 5

    def get_value(self):
        return self.value

class ChildEx1(ParentEx1):
    pass

c1 = ChildEx1()
p1 = ParentEx1()


# 부모 & 자식 모든 속성 출력
print('Ex1 > ', dir(ParentEx1))
print('Ex1 > ', dir(ChildEx1))
"""
Ex1 >  ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'get_value', 'value']
Ex1 >  ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'get_value']
"""


# 부모 & 자식 인스턴스 속성 출력
print('Ex1 > ', ParentEx1.__dict__)
print('Ex1 > ', ChildEx1.__dict__)
"""
Ex1 >  {'__module__': '__main__', '__init__': <function ParentEx1.__init__ at 0x0000028A15F81840>, 'get_value': <function ParentEx1.get_value at 0x0000028A15F818C8>, '__dict__': <attribute '__dict__' of 'ParentEx1' objects>, '__weakref__': <attribute '__weakref__' of 'ParentEx1' objects>, '__doc__': None}
Ex1 >  {'__module__': '__main__', '__doc__': None}
"""
  • 부모 & 자식의 모든 속성을 출력하면 둘의 속성이 동일하게 출력되는 것이 확인된다.
    • 자세히 보면 자식클래스에는 value가 없다. value는 부모 클래스의 __init__ 메소드가 실행되면 생성되는 것이므로 메소드만 물려받은 자식 클래스에는 없는 것이 당연하다.
  • 부모 & 자식의 인스턴스 속성을 출력하면 자식클래스의 인스턴스는 클래스라는 것 외에는 거의 가진것이없다. 이것은 자식클래스가 실제로 상속받는 것은 인스턴스화가 될 때 받는다는 것을 알 수 있다.


Overriding

class ParentEx2():
    def __init__(self):
        self.value = 5

    def get_value(self):
        return self.value

class ChildEx2(ParentEx1):
    def get_value(self):
        return self.value * 10


c2 = ChildEx2()

print('Ex2 > ', c2.get_value())  # 50

위와 같은 방식으로 부모클래스의 메소드를 재정의하여 같은 메소드로 다른 동작방식을 정의할 수 있다.

단! 위에 적었던 오버라이딩의 특징을 유념한 채 재정의 해야한다!



다형성(Polymorphism)

다형성에 대해 보기에 앞서 super()함수에 대해 알아야 한다.

super()

자식클래스에서 부모 클래스를 호출할 때 사용된다.

  • 인자가 있을 때:

    super(자식클래스, 자식클래스의 인스턴스) 형태로 사용한다.
    • super(Child, self).__init__():
      -> Child 클래스의, self 인스턴스의 부모클래스가 가진 __init__메소드를 호출
  • 인자가 없을 때:

    자동으로 현재 클래스와 인스턴스를 참조
    • super().__init__:
      -> 현재 클래스(자식)의 인스턴스의 부모클래스가 가진 __init__메소드를 호출

예시

import datetime

class Logger(object):  # 로그메세지를 출력하는 부모클래스
    def log(self, msg):
        print(msg)

class TimestampLogger(Logger):  # 로그메세지와 시간까지 출력하는 자식클래스 
    def log(self, msg):
        message = "{ts} {msg}".format(ts=datetime.datetime.now(),
                                      msg=msg)
        # super().log(message)
        super(TimestampLogger, self).log(message)

class DateLogger(Logger):  # 더 정확한 시간표시를 해주는 자식클래스
    def log(self, msg):
        message = "{ts} {msg}".format(ts=datetime.datetime.now().strftime('%Y-%m-%d'),
                                      msg=msg)
        # super().log(message)
        super(DateLogger, self).log(message)

l = Logger()
t = TimestampLogger()
d = DateLogger()


l.log("Called logger.")
t.log("Called timestamp logger.")
d.log("Called date logger.")
"""
Called logger.
2024-01-21 10:16:52.064729 Called timestamp logger.
2024-01-21 Called date logger.
"""
profile
공부!

0개의 댓글