ABC(추상 클래스)란 하위 클래스에서 구현해야 할 메서드 목록들이 선언되어 있는 클래스이다. 하위 클래스에 상속의 용도로만 사용된다. (인스턴스 생성 X)
ABC를 상속받는 하위 클래스들은 ABC에서 선언한 추상 메서드들을 구현하는 것이 강제된다.
'추상(Abstract)'이라고 하면 보통 하드웨어에서 멀어지는 것이라고 생각했는데, 소프트웨어 공학적으로는 대충 객체의 구체적인 속성이나 동작을 구현하지 않는다는 의미라고 한다. ABC에서는 이쪽으로 쓰인듯? (참조)
from abc import ABC, abstractmethod
class Guns(ABC):
@abstractmethod
def fire(self):
pass
위와 같이 abc모듈을 이용해서 ABC를 만들 수 있다. @abstractmethod 데코레이터가 달려있는 메서드는 상속받는 하위 클래스가 반드시 구현해야 한다.
class Sniper(Guns):
def __init__(self):
self.sound = "BBANG!!!"
def fire(self):
print(self.sound)
class Revolver(Guns):
def __init__(self):
self.sound = "Tang!"
self.fire_speed = 0.1
def fire(self):
for i in range(5):
print(self.sound)
time.sleep(self.fire_speed)
print(self.sound)
ABC인 Guns의 하위 클래스들을 구현해 보았다. 하위 클래스들에서 fire()함수를 꼭 구현해야한다. 구현하지 않고 인스턴스를 생성하면 TypeError가 발생한다.
print(Guns.__abstractmethods__)
>>> frozenset({'fire'})
ABC에서 어떤 메서드들을 강제하고 있는지 알고싶다면 __abstractmethods__를 통해 알아볼 수 있다.
다형성은 객체지향 프로그래밍의 요소 중 하나로 '같은 이름의 변수 또는 함수가 상황에 따라 다른 동작을 하는 것'을 말한다. 클래스에서는 메서드 오버라이딩(Overriding)이나 오버로딩(Overloading)이 다형성을 나타내는 예시이다.
ABC를 이용하지 않아도 메서드 오버라이딩은 가능하다. 하지만 ABC를 통해 꼭 구현해야하는 메서드들을 강제함으로써 높은 안정성과 함께 다형성을 실현할 수 있다.
raise NotImplementedError를 이용해서도 ABC와 비슷하게 하위 클래스에서 구현해야할 메서드를 강제할 수 있다. 사실 나도 어디선가 저런 코드를 보고 raise NotImplementedError를 주로 사용했었다. 두가지 경우의 차이를 알아보자.
class Guns(ABC):
@abstractmethod
def fire(self):
pass
class Revolver(Guns):
pass
arm = Revolver()
>>> TypeError: Can't instantiate abstract class Revolver with abstract method fire
ABC를 활용하면 메서드 제약을 지키지 않은 하위 클래스가 인스턴스를 생성하는 순간 TypeError가 발생한다.
class Guns():
def fire(self):
raise NotImplementedError
class Revolver(Guns):
pass
arm = Revolver()
arm.fire()
>>> NotImplementedError
raise NotImplementedError를 사용한 경우에는 인스턴스 생성을 할 때는 문제가 없고, 메서드를 실행해야만 에러가 발생한다. ABC를 사용하는 것이 메서드 제약을 지키지 않은 것을 보다 조기에 발견할 수 있다.