파이썬 메소드 오버라이딩/메소드 오버로딩// 인프런 파이썬 강의 Level 3 (2-2)

2022. 1. 14. 22:39
  • 인프런의 '프로그래밍 시작하기:파이썬(Level3)'의 내용을 따라가면서 정리
  • (2-2)에서는 다음에 관하여 다룸
    • 메소드 오버라이딩
    • 메소드 오버로딩

1.메소드 오버라이딩

  • 클래스가 가진 메소드들을 다른(자식) 클래스에 상속(전달)해 주는 것
    • 상속해주는 클래스를 부모클래스 또는 슈퍼클래스라고 부름
    • 상속받는 클래스를 자식클래스 혹은 서브클래스라고 부름
  • 쓰는 이유?
    • 메소드의 재정의 후 사용가능 > 다형성(다양한 방식으로 동작)과 같은 확장성 증가
    • 가독성 증가, 오류가능성 감소, 메소드 이름 절약

 

메소드 오버라이딩 예제

  • 부모클래스를 만들고, 자식클래스에 상속시켰음 (클래스의 인수로 다른 클래스를 받으면 상속이 된다)
  • 기본적으로 모든 메소드는 그대로 상속이 되나, 동일한 이름이 자식클래스에 있을 경우 자식클래스의 메소드를 따라간다.
class ParentEx1():
    def __init__(self):
        self.value = 5
    def get_value(self):
        return self.value
    def get_mul_value(self):
        return self.value * 5
class ChildEx1(ParentEx1):#메소드 오버라이딩. 부모의 모든 변수를 다 가져옴
    def get_mul_value(self):
        return self.value * 10

#인스턴스화
p1 = ParentEx1()
c1 = ChildEx1()

###실행###
print("-----get_value-----")
print("p1 :",p1.get_value())
print("c1 :",c1.get_value())

print("-----get_mul_value-----")
print("p1 :",p1.get_mul_value())
print("c1 :",c1.get_mul_value())

  • get_value 메소드는 부모클래스의 메소드를 그대로 상속받음
  • get_mul_value는 자식클래스에 동일한 이름을 가진 메소드가 있으므로 상속되지 않음. 자식클래스의 메소드가 사용됨(그래서 값이 다르게 출력됨)

 

dir 과 __dict__

  • __dir__은 사용할 수 있는 모든 속성들을 보여줌
  • __dict__는 네임스페이스에 있는 속성들의 키값:벨류값을 보여줌
  • 부모클래스를 상속받는 자식클래스는 부모클래스에 있는 속성들을 사용할 수 있으므로 dir은 동일하게 출력
  • 근데 __dict__에서 출력되는 네임스페이스에는 차이가 있음
    • 이는 자식클래스를 활용해 인스턴스를 만드는 그 시점에 부모클래스로부터 속성을 상속받아서 인스턴스에 전달해주기 때문임
    • 즉 어차피 부모클래스로부터 상속받아 사용할 수 있으므로 dir에 나오지만 일단 현재 가지고 있는 것은 아니므로 네임스페이스에 없어서 __dict__에 나오지 않음
# dir
print('Parent dir > ', dir(ParentEx1))
print('Child dir > ', dir(ChildEx1))
print()

# __dict__
print('Parent dict > ', ParentEx1.__dict__)
print('Child dict> ', ChildEx1.__dict__) 
#자식클래스는 네임스페이스에 내용이 많이 없음
#인스턴스가 생성될 때 네임스페이스의 속성들까지 상속받는 것이기 때문

  • dir은 차이가 없으나, __dict__의 출력값에 차이가 있다

 

메소드 오버라이딩을 활용한 다형성

  • 메소드 오버라이딩의 장점 중 하나는 다형성
  • 말이 어려운데, 쉽게 말해서 부모클래스에서 정의한 메소드를 기반으로 다양하게 수정하여 확장시켜나갈 수 있다는 것이다.
  • 보통 super()가 활용된다. super()는 부모클래스의 메소드를 호출하여 사용할 수 있다.
    • super는 첫번째 인자로 현재 자신이 속한 메소드, 두번째 인자로 인스턴스 객체(보통 self)를 넣어줄 수 있는데, 비워두면 자동으로 알아서 그렇게 채워줌(밑의 예제 참고)
import datetime

class Logger():
    def parent_log(self,msg):
        print(msg)
        
class Logger_with_time_detailed(Logger):
    def log(self,msg):
        now_time = datetime.datetime.now()
        message = f'time : {now_time}, msg = {msg}'
        super().parent_log(message)
        
class Logger_with_time_ymd(Logger):
    def log(self,msg):
        now_time = datetime.datetime.now().strftime('%y-%m-%d')
        message = f'time : {now_time}, msg = {msg}'
        super(Logger_with_time_ymd,self).parent_log(message)
        #super().parent_log(message)로 해도 상관없다

#인스턴스
c1 = Logger_with_time_detailed()
c2 = Logger_with_time_ymd()

#####실행#####
c1.log("hi!")
c2.log("hi!")

  • Logger라는 부모클래스와, 부모클래스를 상속받는 자식클래스를 두개 작성
  • 두 자식클래스의 메소드들 모두, super()를 활용하여 부모클래스의 parent_log 메소드를 사용해 message를 출력한다(자식클래스의 메소드에는 print가 없음!!)
  • 이렇게 상위클래스의 메소드를 가져와서 입맛에 맞게 수정할 수 있는게 메소드 오버라이딩의 장점인 다형성(확장성)임

 

2.메소드 오버로딩

  • 메소드 오버로딩은 '하나의 메소드가 매개변수에 따라 다른 기능으로 동작하게 함.' 즉 하나의 이름으로 상황에 따라 다르게 동작하는 메소드를 만드는 것임
  • 메소드 오버라이딩은 '클래스의 상속시에 상속받은 메소드를 자식클래스에서 수정하는 것'
  • 메소드 오버로딩은 '하나의 메소드에게 다형성을 부여하는 것'(메소드와 매개변수사이의 일임)

 

메소드 오버로딩 - 직접구현

  • 과거에는 datatype이나 매개변수의 개수와 같은 것을 메소드의 인자로 함께 줘서 datatype의 종류나 개수에 따라 다른 작업이 수행되는 방식을 사용했었음
  • 이런식으로 구현되어있는 코드가 여전히 많음
class SampleA():
    def add(self,datatype,*args):
        if datatype == 'int':
            return sum(args)
        if datatype == 'str':
            return ''.join([x for x in args])
     
#인스턴스     
a = SampleA()

#####실행#####
print(a.add('int',1,2,3,4)) #10 출력
print(a.add('str',"a","p","p","l","e")) #apple 출력

 

메소드 오버로딩 - multipled dispatch

  • 최근에 multipled dispatch라는 기능이 추가되면서 , 얘를 활용해서 메소드 오버로딩을 할 수 있음
  • 동일한 이름을 가진 메소드에 자료형의 종류와 개수에 대한 정보를 데코레이터해줌
  • 메소드를 호출할 때, 인자로 받는 자료형의 종류 및 개수와 데코레이터된 정보가 일치하는 메소드가 호출됨
from multipledispatch import dispatch
class SampleB():
        
    @dispatch(int,int,int)
    def product(x,y,z):
        print("int!")
        return x * y * z
    
    @dispatch(float,float,float)
    def product(x,y,z):
        print("float!")
        return x + y + z
    
#인스턴스
b = SampleB()
        
#####출력#####
print(b.product(4,5,6))
print(b.product(4.0,5.0,6.0))

  • @dispatch를 활용하여, int자료형들이 인자로 들어올 경우 서로 곱해지는 product 메소드가, float자료형들이 인자로 들어올 경우 서로 더해주는 product 메소드가 호출되도록 하였음

BELATED ARTICLES

more