추상화 클래스 모듈인 ABC와 상속 받을 클래스의 함수 내에서 구현을 강제하는 NotImplementedError은 기능상 동일합니다. 상속을 받고 해당 함수를 정의하지 않으면 에러를 발생 시킵니다. 여기서 미묘한 차이가 나오는데 어떤 차이가 있는지 설명합니다.

추상화 클래스 ABC 예시

ABC 모듈은 파이썬 내장 모듈로 기능을 제공하고 있습니다.

from abc import ABCMeta, abstractmethod, ABC


class DBManager(metaclass=ABCMeta):
    @abstractmethod
    def connect(self):
        pass

#  또는 


class DBManager(ABC):
    @abstractmethod
    def connect(self):
        pass


class MySQL(DBManager):
    def connect(self):
        print('MySQL 연결')


class PostgreSQL(DBManager):
    def connect(self):
        print('PostgreSQL 연결')


PostgreSQL().connect()
MySQL().connect()

NotImplementedError 예시

NotImplementedError는 위 ABC와 동일한 형태를 띄고 있습니다.

class DBManager:
    def connect(self):
        raise NotImplementedError('구현해주세요')


class PostgreSQL(DBManager):
    def connect(self):
        print('PostgreSQL 연결')


class MySQL(DBManager):
    def connect(self):
        print('MySQL 연결')



PostgreSQL().connect()
MySQL().connect()

차이점

위 예시만 봤을 땐 크게 차이가 없는 것처럼 보이지만 미묘하게 다른 점을 아래에서 설명합니다.

1. 추상화 클래스 abc는 선언할 수 없음

abc 모듈은 단독으로 선언을 할 수 없지만 NotImplementedError를 구현한 클래스는 기본 클래스이므로 선언이 가능합니다.

from abc import ABCMeta, abstractmethod


class DBManager2(metaclass=ABCMeta):
    @abstractmethod
    def connect(self):
        pass

class DBManager:
    def connect(self):
        raise NotImplementedError('구현해주세요')


a = DBManager() # 인스턴스화 가능
b = DBManager2() # TypeError: Can't instantiate abstract class DBManager2 with abstract methods connect


정확하게는 abc 모듈의 abstractmethod 어노테이션이 추가가 되어 있으면 위와 같은 오류가 발생합니다. 이는 파생 클래스 구현을 위한 추상화 클래스 기능을 제공하는 역할만을 하기 때문입니다.

2. 필수 함수 미 구현 시, 에러 발생 시점이 다름

먼저 abc 모듈을 사용한 클래스의 함수 미구현 시, 발생하는 에러입니다.

from abc import ABCMeta, abstractmethod


class DBManager2(metaclass=ABCMeta):
    @abstractmethod
    def connect(self):
        pass


class PostgreSQL(DBManager2):
    def insert(self):
        print('PostgreSQL insert')


PostgreSQL().insert() # TypeError: Can't instantiate abstract class PostgreSQL with abstract methods connect


에러의 발생 시점은 해당 모듈이 import 될 때, 잡아내고 발생시킵니다. 즉, connect()가 구현되어 있지 않고 해당 함수를 호출하지 않아도 미 구현이라는 에러가 발생합니다.


다음은 NotImplementError를 사용하여 해당 함수를 미구현한 예시입니다.

class DBManager2:
    def connect(self):
        raise NotImplementedError('구현필수')


class PostgreSQL(DBManager2):
    def insert(self):
        print('PostgreSQL insert')


PostgreSQL().insert() # 에러 발생 X
PostgreSQL().connect()
#     raise NotImplementedError('구현필수')
# NotImplementedError: 구현필수


NotImplementedError를 사용하면 exception이 들어간 함수를 직접 호출할 때, 에러가 발생합니다. 즉, 런타임 상황에서 해당 함수가 구현이 되어 있는지 없는지 확인한 다음 에러를 발생하게 됩니다. 위 예시에서 connect()를 호출하지 않고 insert()만 호출하면 에러가 발생하지 않는 것을 볼 수 있습니다.

요약

추상화 클래스를 엄격하게 구현, 사용하고 싶으면 abc 모듈을 사용해 구현하는 방향이 좀 더 안전한 방식으로 보임