abc — 추상 베이스 클래스

abc — 추상 베이스 클래스 (Abstract Base Classes)

이 모듈은 PEP 3119에서 개요된 대로, Python에서 **추상 베이스 클래스(ABC, Abstract Base Class)**를 정의하는 기반을 제공해요. collections 모듈에는 ABC에서 파생된 구상 클래스들이 있고, collections.abc 서브모듈에는 클래스나 인스턴스가 특정 인터페이스를 제공하는지(예: 해시 가능한지, 매핑인지) 검사하는 데 쓸 수 있는 ABC들이 있어요.

출처: Python 표준 라이브러리

본문

이 모듈은 PEP 3119에서 개요된 대로 Python에서 추상 베이스 클래스(ABCs)를 정의하기 위한 기반을 제공해요. 왜 Python에 추가됐는지는 PEP를 보시면 돼요. (숫자 타입 계층에 대한 ABC 기반의 내용은 PEP 3141과 numbers 모듈도 참고하세요.)

collections 모듈에는 ABC에서 파생된 몇몇 구상(concrete) 클래스가 있는데, 당연히 더 파생시킬 수 있어요. 게다가 collections.abc 서브모듈에는 클래스나 인스턴스가 특정 인터페이스를 제공하는지 — 예를 들어 해시 가능한지(hashable)나 매핑인지 — 검사하는 데 쓸 수 있는 ABC들이 있어요.

이 모듈은 ABC를 정의하는 메타클래스 ABCMeta와, 상속을 통해 더 간편하게 ABC를 정의할 수 있게 해 주는 헬퍼 클래스 ABC를 제공해요.

class abc.ABC

메타클래스로 ABCMeta를 가지는 헬퍼 클래스예요. 이 클래스로 그냥 ABC에서 상속받기만 하면 추상 베이스 클래스를 만들 수 있어서, 때때로 헷갈리는 메타클래스 사용법을 피할 수 있어요.

from abc import ABC

class MyABC(ABC):
    pass

참고로 ABC의 타입은 여전히 ABCMeta예요. 그래서 ABC에서 상속받는 것도 메타클래스 사용에 대한 평소의 주의가 필요해요. 다중 상속이 메타클래스 충돌을 일으킬 수 있으니까요. 메타클래스 키워드와 ABCMeta를 직접 써서 추상 베이스 클래스를 만들 수도 있어요.

from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    pass

3.4 버전에서 추가.

class abc.ABCMeta

추상 베이스 클래스(ABCs)를 정의하기 위한 메타클래스예요.

이 메타클래스로 ABC를 만들어요. ABC는 직접 서브클래싱될 수 있고, 그러면 믹스인(mix-in) 클래스처럼 작동해요. 또한 관련 없는 구상 클래스(내장 클래스까지도)와 관련 없는 ABC를 "가상 서브클래스(virtual subclass)"로 등록할 수 있어요. 그러면 그들과 그들의 자손은 내장 issubclass() 함수에 의해 등록 ABC의 서브클래스로 간주되지만, 등록 ABC는 그들의 **MRO(Method Resolution Order)**에 나타나지 않고, 등록 ABC가 정의한 메서드 구현도 호출할 수 없어요(super()로조차도).

ABCMeta를 메타클래스로 가진 클래스들은 다음 메서드를 가져요.

register(subclass)

subclass를 이 ABC의 "가상 서브클래스"로 등록해요. 예를 들면:

from abc import ABC

class MyABC(ABC):
    pass

MyABC.register(tuple)

assert issubclass(tuple, MyABC)
assert isinstance((), MyABC)

3.3 버전 변경: 등록된 서브클래스를 반환해서 클래스 데코레이터로 쓸 수 있게 됐어요. 3.4 버전 변경: register() 호출을 감지하려면 get_cache_token() 함수를 사용할 수 있어요.

추상 베이스 클래스에서 이 메서드를 오버라이드할 수도 있어요.

__subclasshook__(subclass)

(클래스 메서드로 정의해야 해요.)

subclass가 이 ABC의 서브클래스로 간주되는지 확인해요. 즉, ABC의 서브클래스로 간주하고 싶은 모든 클래스에 register()를 호출할 필요 없이 issubclass()의 동작을 더 커스터마이즈할 수 있어요. (이 클래스 메서드는 ABC의 __subclasscheck__() 메서드에서 호출돼요.)

이 메서드는 True, False, 또는 NotImplemented를 반환해야 해요. True를 반환하면 subclass는 이 ABC의 서브클래스로 간주돼요. False를 반환하면 원래라면 서브클래스여도 ABC의 서브클래스로 간주되지 않아요. NotImplemented를 반환하면 서브클래스 검사가 평소의 메커니즘으로 계속돼요.

이 개념들을 잘 보여주는 ABC 정의 예시를 볼게요.

class Foo:
    def __getitem__(self, index):
        ...
    def __len__(self):
        ...
    def get_iterator(self):
        return iter(self)

class MyIterable(ABC):

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    def get_iterator(self):
        return self.__iter__()

    @classmethod
    def __subclasshook__(cls, C):
        if cls is MyIterable:
            if any("__iter__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

MyIterable.register(Foo)

ABC MyIterable은 표준 이터러블 메서드인 __iter__()를 추상 메서드로 정의해요. 여기 주어진 구현은 여전히 서브클래스에서 호출할 수 있어요. get_iterator() 메서드도 MyIterable 추상 베이스 클래스의 일부지만, 비추상 파생 클래스에서 오버라이드할 필요는 없어요.

여기 정의된 __subclasshook__() 클래스 메서드는, 자기 __dict__(또는 __mro__ 리스트를 통해 접근하는 베이스 클래스 중 하나의 __dict__)에 __iter__() 메서드가 있는 어떤 클래스든 MyIterable로도 간주된다고 말해요.

마지막 줄은 FooMyIterable의 가상 서브클래스로 만들어요. Foo__iter__() 메서드를 정의하지 않지만(__len__()__getitem__()으로 정의되는 구식 이터러블 프로토콜을 사용) 상관없이요. 이건 Foo의 메서드로 get_iterator를 제공하지 않으므로, 그건 별도로 제공해야 한다는 점을 기억하세요.

@abc.abstractmethod

추상 메서드를 나타내는 데코레이터예요.

이 데코레이터를 쓰려면 클래스의 메타클래스가 ABCMeta이거나 그것에서 파생돼야 해요. ABCMeta에서 파생된 메타클래스를 가진 클래스는 추상 메서드와 프로퍼티를 모두 오버라이드하지 않으면 인스턴스화할 수 없어요. 추상 메서드는 일반적인 'super' 호출 메커니즘으로 호출할 수 있어요. @abstractmethod는 프로퍼티와 descriptor의 추상 메서드를 선언하는 데 쓸 수 있어요.

클래스에 추상 메서드를 동적으로 추가하거나, 만들어지고 난 뒤 메서드나 클래스의 추상 상태를 수정하는 것은 update_abstractmethods() 함수로만 지원돼요. @abstractmethod는 정규 상속으로 파생된 서브클래스에만 영향을 주고, ABC의 register() 메서드로 등록된 "가상 서브클래스"에는 영향을 주지 않아요.

@abstractmethod를 다른 메서드 descriptor와 조합해 사용할 때는, 아래 사용 예시처럼 가장 안쪽 데코레이터로 적용해야 해요.

class C(ABC):
    @abstractmethod
    def my_abstract_method(self, arg1):
        ...
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, arg2):
        ...
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(arg3):
        ...

    @property
    @abstractmethod
    def my_abstract_property(self):
        ...
    @my_abstract_property.setter
    @abstractmethod
    def my_abstract_property(self, val):
        ...

    @abstractmethod
    def _get_x(self):
        ...
    @abstractmethod
    def _set_x(self, val):
        ...
    x = property(_get_x, _set_x)

추상 베이스 클래스 메커니즘과 올바르게 상호 운용되려면, descriptor는 __isabstractmethod__로 자신이 추상임을 나타내야 해요. 일반적으로 이 속성은 descriptor를 구성하는 메서드 중 하나라도 추상이면 True여야 해요. 예를 들어 Python의 내장 @property는 다음처럼 동작해요.

class Descriptor:
    ...
    @property
    def __isabstractmethod__(self):
        return any(getattr(f, '__isabstractmethod__', False) for
                   f in (self._fget, self._fset, self._fdel))

참고: Java의 추상 메서드와 달리, 이런 추상 메서드들은 구현을 가질 수 있어요. 이 구현은 오버라이드한 클래스에서 super() 메커니즘으로 호출할 수 있어요. 협력적 다중 상속을 쓰는 프레임워크에서 super 호출의 종착점으로 유용할 수 있죠.

레거시 데코레이터 (Legacy decorators)

abc 모듈은 다음의 레거시 데코레이터도 지원해요.

@abc.abstractclassmethod

내장 classmethod의 서브클래스로, 추상 classmethod를 나타내요. 그 외에는 @abstractmethod와 비슷해요.

이 특별한 경우는 **3.3부터 폐기(deprecated)**됐어요. @classmethod 데코레이터가 추상 메서드에 적용되면 이제 올바르게 추상으로 식별되기 때문이에요.

class C(ABC):
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, arg):
        ...

3.2 버전에서 추가, 3.3 버전부터 폐기.

@abc.abstractstaticmethod

내장 staticmethod의 서브클래스로, 추상 staticmethod를 나타내요. 그 외에는 @abstractmethod와 비슷해요. @staticmethod 데코레이터가 추상 메서드에 적용되면 이제 올바르게 추상으로 식별되므로 3.3부터 폐기됐어요.

class C(ABC):
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(arg):
        ...

3.2 버전에서 추가, 3.3 버전부터 폐기.

@abc.abstractproperty

내장 property의 서브클래스로, 추상 프로퍼티를 나타내요. @property, @property.getter, @property.setter, @property.deleter가 이제 @abstractmethod와 함께 쓸 수 있으므로 3.3부터 폐기됐어요.

class C(ABC):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...

위 예시는 읽기 전용 프로퍼티를 정의해요. 기반 메서드 중 하나 이상을 적절히 추상으로 표시하면 읽기-쓰기 추상 프로퍼티도 정의할 수 있어요.

class C(ABC):
    @property
    def x(self):
        ...

    @x.setter
    @abstractmethod
    def x(self, val):
        ...

일부 구성 요소만 추상이라면, 서브클래스에서 구상 프로퍼티를 만들 때 그 구성 요소들만 업데이트하면 돼요.

class D(C):
    @C.x.setter
    def x(self, val):
        ...

abc.get_cache_token()

현재 추상 베이스 클래스 캐시 토큰을 반환해요. 토큰은 가상 서브클래스의 추상 베이스 클래스 캐시의 현재 버전을 식별하는 불투명 객체(동등성 검사를 지원)예요. 토큰은 어떤 ABC에서든 ABCMeta.register()를 호출할 때마다 바뀌어요.

3.4 버전에서 추가.

abc.update_abstractmethods(cls)

추상 클래스의 추상 상태를 다시 계산하는 함수예요. 클래스의 추상 메서드가 만들어지고 난 뒤 구현되거나 변경됐다면 이 함수를 호출해야 해요. 보통은 클래스 데코레이터 안에서 이 함수를 호출해요. cls를 반환해서 클래스 데코레이터로 쓸 수 있어요. clsABCMeta의 인스턴스가 아니면 아무것도 하지 않아요.

참고: 이 함수는 cls의 슈퍼클래스가 이미 업데이트됐다고 가정해요. 서브클래스는 업데이트하지 않아요.

3.10 버전에서 추가.