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로도 간주된다고 말해요.
마지막 줄은 Foo를 MyIterable의 가상 서브클래스로 만들어요. 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를 반환해서 클래스 데코레이터로 쓸 수 있어요. cls가 ABCMeta의 인스턴스가 아니면 아무것도 하지 않아요.
참고: 이 함수는
cls의 슈퍼클래스가 이미 업데이트됐다고 가정해요. 서브클래스는 업데이트하지 않아요.3.10 버전에서 추가.