3-4. Method Overriding

uoayop·2021년 3월 21일
0

Leaf와 Python

목록 보기
13/21
post-thumbnail

메소드 오버라이딩, 오버로딩

자바를 배웠다면 한번쯤은 들어봤을 단어이다. 객체 지향언어에서 특히 많이 사용된다.

개인적으로 이름이 비슷해서 너무 헷갈렸던 개념이었는데, 확실히 정리가 되었다.

  • 오버라이딩은 부모 클래스의 메소드를 자식 클래스에서 재정의,
    오버로딩은 오버라이딩과 비슷하지만 메소드 파라미터 기반 호출 방식

Method Overriding

  • 오늘의 키워드
    • Overriding, OOP, 다형성 예제

오버라이딩 대체 왜 쓰는거죠? 🤔

1. 서브 클래스 (자식) 에서 슈퍼 (부모) 클래스를 호출 후 사용 가능

2. 메소드 재정의 후 사용 가능

3. 부모 클래스의 메소드를 추상화 후 사용가능

  • 재정의와 비슷하지만 구조적 접근이 가능하다.

4. 확장 가능, 다형성이 존재

  • 다형성? 다양한 방식으로 동작한다는 의미다.
    부모에서 하나를 만들어도 자식에서 어떻게 정의하느냐에 따라 달라진다.

5. 가독성 증가, 오류 가능성 감소

  • 개발은 나 혼자 개발하는 거 아니다.
    오버라이딩을 통해 유지보수 쉬워진다.

6. 메소드 이름 절약

  • 코딩을 하다 보면 메소드 동작에 맞지 않는 네이밍을 할 때가 있다.
    게다가 똑같은 기능을 하는 메소드인데 이름을 이상하게 짓게 되기도 한다.
  • 부모 클래스에서 메소드의 이름을 정의해놓고, 자식 클래스에서 상속 받아서 하나의 메소드의 이름으로 사용이 가능하다.

기본 Overriding 예제

  • 클래스 b가 클래스 a 를 상속 받기 위해선, 클래스를 선언할 때 인자로 함께 넣어주면 된다.
  • b(a)
class ParentEx1():
    def __init__(self):
        self.value = 5

    def get_value(self): #내부 인스턴스 value를 반환하는 함수
        return self.value
    
# 부모 클래스를 상속 받음 
class ChildEx1(ParentEx1):
    pass

c1 = ChildEx1()
p1 = ParentEx1()

# 부모 클래스 메소드 호출
# 자식 클래스엔 정의가 안된 함수로 값을 출력 가능하다.
print("ex 1 > ",c1.get_value())
  • dir을 통해 자식 객체의 내부를 살펴보자
    • 부모 객체에 정의된 함수와 변수가 자식 객체에도 존재한다.
    • 부모와 자식 객체의 모든 속성이 같은 것을 확인할 수 있다.
# c1의 모든 속성 출력
print("ex 1 > ",dir(c1))
# [ ... , 'get_value', 'value']

# 부모와 자식의 모든 속성 출력
# print('ex 1 >', dir(ParentEx1))
# print('ex 1 >', dir(ChildEx1))
  • 그렇다면 클래스.__dict__ 로 네임스페이스를 확인해보자 ⭐️

    name space

    name들의 공간으로, 변수 이름과 객체가 연결된 공간을 떠올리면 된다.

    • 자식의 네임스페이스에는 우리가 부모 클래스에 정의한 변수와 함수가 없다.
    • 따라서 선언이 아닌, 인스턴스화 되는 시점에 자식에 담기는 것을 알 수 있다!
# 부모와 자식의 네임스페이스 확인 ( 중요 )
print('ex 1 >', ParentEx1.__dict__)
print('ex 1 >', ChildEx1.__dict__)

# ---- 출력 ----
ex 1 > Parent.__dict__ : 
{'__module__': '__main__', '__init__': 
<function ParentEx1.__init__ at 0x7fdfa9299700>, 
'get_value': <function ParentEx1.get_value at 0x7fdfa92514c0>, 
'__dict__': <attribute '__dict__' of 'ParentEx1' objects>, 
'__weakref__': <attribute '__weakref__' of 'ParentEx1' objects>, '__doc__': None}

ex 1 > Child.__dict__ :
{'__module__': '__main__', '__doc__': None}

Overriding 재정의 예제

  • 부모의 뼈대를 가져와서 자식이 다시 정의해서 사용한다.
class ParentEx2():
    def __init__(self):
        self.value = 5

    def get_value(self): 
        return self.value

# 부모 클래스를 상속 받아서, 같은 함수 이름을 이용해 재정의한다.
class ChildEx2(ParentEx2):
    def get_value(self):
        # 부모 클래스의 인스턴스 값에 10을 곱해서 리턴한다.
        return self.value * 10

# 자식 메소드 재정의 후 호출
c2 = ChildEx2()
print('ex 2 > ',c2.get_value())

Overriding 다형성 예제

  • 원하는 상황에 로그를 출력하는 클래스를 만들어보자.
  • 시간과 날짜, 날짜만 출력해주는 클래스를 만드려고 한다.
import datetime

class Logger(object):
    def log(self, msg):
        print(msg)
  • 부모 클래스에 print문이 있다.
    따라서 출력을 하려면 자식 클래스에서 상속 받은뒤에 log 함수로 부모 클래스에 올려야 한다.
  • 시간과 날짜를 출력하는 TimestampLogger 클래스, 날짜만 출력해주는 DateLogger 클래스가 Logger 클래스를 상속 받도록 정의해주었다.
# 상속 받음
class TimestampLogger(Logger):
    def log(self,msg):
        message = "{ts} {msg}".format(ts=datetime.datetime.now(), msg=msg)
        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(DateLogger, self).log(message) 

super 사용하기

부모 클래스에 있는 요소를 사용하기 위해선 super를 사용해야 한다.
1. FM 식으로 코딩하기

  • super 인자에 두가지 요소를 넣어줌으로서 더 꼼꼼하게 코딩을 할 수 있다.
  • 어디에서 호출을 하는지 정확히 알 수 있다.
    super(내 자신 클래스 이름, 클래스이자 자식의 인스턴스)
class TimestampLogger(Logger):
    def log(self,msg):
        message = "{ts} {msg}".format(ts=datetime.datetime.now(), msg=msg)
        super(TimestampLogger, self).log(message) 
        # super 인자 : (내 자신 클래스 이름, 클래스이자 자식의 인스턴스)

2. 조금 더 편하게 코딩하기

  • super에 인자를 주지 않아도 실행된다.
class TimestampLogger(Logger):
    def log(self,msg):
        message = "{ts} {msg}".format(ts=datetime.datetime.now(), msg=msg)
        super().log(message)
  • 전체 코드
import datetime

class Logger(object):
    def log(self, msg):
        print(msg)

# 상속 1
class TimestampLogger(Logger):
    def log(self,msg):
        message = "{ts} {msg}".format(ts=datetime.datetime.now(), msg=msg)
        super(TimestampLogger, self).log(message) 
# 상속 2
class DateLogger(Logger):
    def log(self,msg):
        message = "{ts} {msg}".format(ts=datetime.datetime.now().strftime('%Y-%m-%d'), msg=msg)
        super(DateLogger, self).log(message) 

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

l.log('Called loger.')
# 출력: Called loger.
t.log('Called timestamp loger.')
# 출력: 2021-03-21 16:02:41.588938 Called timestamp loger.
d.log('Called date loger.')
# 출력: 2021-03-21 Called date loger.
  • 부모 클래스에 정의한 하나의 log 함수를 이용해서,
    자식 클래스의 다양한 형태를 출력한 것을 확인할 수 있다.

출처: 인프런 - 모두를 위한 파이썬 : 필수 문법 배우기 Feat. 오픈소스 패키지 배포 (Inflearn Original)

profile
slow and steady wins the race 🐢

0개의 댓글