본문 바로가기
Programming/Python

[Python] Abstract Classes(추상클래스)의 객체 생성은 가능한가?

by a voyager 2020. 11. 6.
728x90
반응형
728x90

Python: Abstract Classes(추상클래스)의 객체 생성은 가능한가?

 

- 클래스는 인스턴스의 템플레이트: 일반 클래스는 객체 생성 가능 

- 추상 클래스 (ABC)는 클래스의 템플레이트: 추상 클래스는 객체를 바로 만들 수 없다.  

 

 

 

도입 

객체지향 언어의 핵심은 클래스이다. 클래스를 잘 다룬다면 전역 변수 사용을 없앨 수 있을 뿐 아니라 코딩의 효율을 높일 수 있다. 이점이 많은 만큼 클래스는 공부해야 할 특징이 많다. 그중 핵심적인 것이 상속(inheritance)이라는 것이다. 이것은 이름에서도 알 수 있듯이 클래스의 속성 및 함수들을 다른 클래스에게 사용할 수 있도록 물려주는 것이다. 이것은 다른 포스팅에서 자세히 다루도록 하겠다.

 

이와 더불어 추상클래스라는 종류가 있다. 클래스인데 추상적이다...라는 말은 이렇게 정의된다. 추상 클래스는 하나 이상의 추상 메서드를  가지며 자식 클래스에게 그 매서드의 사용을 강제하도록 설계된 클래스이다. 추상 클래스는 일반 클래스와 달리 스스로 객체(instance)를 만들 수 없다. 이것은 클래스의 본래 작동 방식과 다르다.

즉, 일반적인 클래스를 특정한 객체를 찍어 낼 수 있는 주조(template)라고 한다면, 그것으로 만들어 낸 객체는 클래스에서 포함하는 기능(매서드)을 수행할 수 있어야 한다.

 

예를 들어, '사람' 이라는 클래스로 '홍길동'이라는 객체를 만들어 냈으면, '사람'이 하는 기능, 말하기, 걷기, 먹기, 등등의 행동을 할 수 있어야 한다는 것이다. 이런 행동들이 매서드이다. 즉, 홍길동 = 사람() 으로 찍어 냈으면, 홍길동.걷다(), 홍길동.먹다() 이런 식으로 매서드를 실행할 수 있어야 한다. 이런 매서드들은 모두 사람 클래스에 정의된 대로 실행되는 실제 함수이다. 

 

추상 클래스로 다시 돌아오면, 앞서 얘기 했듯이 이것은 객체를 만들 수 없다. 그럼 여기서 할 수 있는 질문이 객체를 만들 수 없다면 매서드를 어떻게 실행 할 수 있는가이다. 먼저 답 부터 말하자면, 추상 클래스를  일반 클래스에게 상속함으로써 객체를 생성할 수 있다. 이 포스팅에서는 이것이 어떻게 구현되는지 살펴보고, 추상 클래스에 대한 이해의 폭을 넓히도록 하겠다. 


추상 클래스는 객체화 할 수 있는가? 

파이썬 추상 클래스는 추상 매서드 (method)를 갖고 있는 클래스를 일컫는다. 도입부에서 언급했듯이 추상 클래스는 스스로 객체를 만들 수 없다. 그러므로 자기의 매서드도 실행 할 수 없다.

 

예를 들어 다음과 같이 추상 클래스를 선언했다고 하자. 6번째 줄에서 객체를 생성하려고 하면 에러를 낸다. 

class AbstractClass:

    def do_something(self):
        pass

a = AbstractClass() # 이렇게 객체는 만들 수 없다                                              

# 따라서 아래처럼 매서드도 실행할 수 없다. 
a.do_something()  

 

사실 위의 예제는 실제 추상 클래스의 구현 방식은 아닌 설명을 위한 illustration정도라고 보면 된다. 


추상 클래스 구현을 위한 모듈 로드 

자 그럼 이제 파이썬의 실제 추상 클래스를 만들어 실행해 보도록 하자. 우선 알아둘 것은 추상 클래스는 일반 클래스처럼 프로그래머 임의로 만들 수 있는 클래스가 아니라는 것이다. 파이썬에서 제공하는 모듈을 사용해서만 정의할 수 있다. 그것은 Abtract Base Classes(ABC)라는 모듈을 통해 추상 클래스를 정의한다. 아래 예제는 파이썬에서 실제로 추상 클래스를 정의하는 코드이다. 

from abc import ABC, abstractmethod

class AbstractClassExample(ABC):
     
    def __init__(self, value):
        self.value = value
        # 생성자에 ABC의 __init__() 메서드를 오버라이드 한다.   
        super().__init__()
    
    # 추상 메서드를 지정하는 데코레이터 (decorator)
   @abstractmethod 
    def do_something(self):
        pass

 

위 코드에서 ABC와 abstractmethod라는 모듈을 읽었다. 이것이 부모 클래스가 되어 추상 클래스를 포함하는 AbstractClassExample 클래스에 상속된다. 세번째 줄에서 ABC를 상속 받은 것을 볼 수 있다.


라인 by 라인으로 의미를 보도록 해보자.

 

- line 5 : 자식(derived) 클래스의 생성자를 호출 한다. 호출시 value라는 값을 받는다. 

- line 6:  입력 받은 value를 자식 클래스의 속성으로 선언한다.

- line 8: 상속받은 ABC 클래스의 생성자를 superimposing 해준다. 이때, 상속받는 클래스는 하나이기 때문에 super()에 클래스의 이름을 생략할 수 있다. 

- line 11-13: do_something()이란 매서드에 @abstractmethod로 포장함으로써 추상 매서드로 정의한다. 

 


여기에서 질문은 저 추상 매서드를 어떻게 실행할 것인가이다. 앞에서 추상 클래스로 직접 객체 생성은 할 수 없지만 다른 클래스로 상속함으로서 가능하다고 하였다. 추상 클래스는 주로 상속되는 부모 클래스로 사용된다. 따라서 아래와 같이 AbstractClassExample을 상속받는 DoAdd42라는 클래스를 만들고, x라는 객체를 생성해보자.

class DoAdd42(AbstractClassExample):
    pass

x = DoAdd42(4)

 

이것은 다음과 같이 추상 매서드를 갖는 클래스를 객체화할 수 없다는 에러를 낸다.

 

 

이것을 해결하기 위해서는 DoAdd42 클래스에 추상 매서드를 overriding 하는 매서드를 만들어여한다. 어떻게 하는지 아래에서 보도록 하자.


Overriding (덮어쓰기)를 통한 추상 매서드의 실행 

 

추상 클래스를 상속받는 일반 클래스를 만든다. 그리고 그 클래스에 실행하고자 하는 추상 매서드와 같은 이름의 메서드를 만든다. 이것을 overiding (덮어쓰기)이라고 한다. 이렇게 만들어진 일반 클래스를 객체화하고 그 안의 매서드를 실행하는데에는 아무런 문제가 없다. 

 

아래 예제는 두 일반 클래스 (DoAdd42, DoMul42)를 통해 결과를 확인한 것이다. DoAdd42는 입력 받은 값에 42를 더하는 매서드이고 DoMul42는 42를 곱하는 기능을 한다. 

class DoAdd42(AbstractClassExample):

    def do_something(self):  # <---------- overridden method 
        return self.value + 42
    
class DoMul42(AbstractClassExample):
       
    def do_something(self):  # <---------- overridden method  
        return self.value * 42
    
x = DoAdd42(10)
y = DoMul42(10)

print(x.do_something())      # 52 
print(y.do_something())      # 420

 

 

그럼 추상 매서드는 자기가 속해있는 추상 클래스에서 실행할 순 없는 것인가? 라는 질문을 할 수 있다. 그렇지 않다. 다만 추상 클래스의 직접적인 객체를 통한 실행이 안될 뿐이다. 추상클래스를 상속받은 일반클래스를 통해 추상매서드를 실행할 수 있다. 여기에서도 추상 매서드 를 오버라이딩 해주는 것을 주목한다. 아래 예제에서 확인해보자 

	
from abc import ABC, abstractmethod

class AbstractClassExample(ABC):
    
    @abstractmethod
    def do_something(self):
        print("Some implementation!")
        
class AnotherSubclass(AbstractClassExample):

    def do_something(self):
        super().do_something()
        print("The enrichment from AnotherSubclass")
        
x = AnotherSubclass()
x.do_something()


'''
    (결과)

    Some implementation!
    The enrichment from AnotherSubclass
'''

 

위의 12번째 줄에서 overridden된 do_someting()매서드 안에 super()를 이용해 AbstractClassExample내의 추상 매서드를 중첩시켜 주었다. 그러므로 부모 클래스의 추상 매서드를 실행할 수 있다. 

 

 

(최종 업데이트 2020.09.08)

728x90
반응형

댓글