abc --- 추상 베이스 클래스¶
소스 코드: Lib/abc.py
이 모듈은, PEP 3119에서 설명된 대로, 파이썬에서 추상 베이스 클래스 (ABC) 를 정의하기 위한 기반 구조를 제공합니다; 이것이 왜 파이썬에 추가되었는지는 PEP를 참조하십시오. (ABC를 기반으로 하는 숫자의 형 계층 구조에 관해서는 PEP 3141과 numbers 모듈을 참고하십시오.)
flowdas
ABC 의 가장 중요한 목적은 isinstance() 를 통해 객체의 형을 파악하는 표준적인 방법을
제공하는 것입니다. 표준적인 절차가 필요한 이유는, 형 계층 구조가 라이브러리들이 정의하는 클래스들과
확장형들에 의해 계속 확장될 것이고, 검사하고자 하는 형을 좀 더 추상화함으로써 이런 확장이 과거 호환성
있는 형태가 되도록 만들기위함입니다. 이 작업의 일환으로 issubclass() 도 확장 가능하도록
만드는 "가상 서브 클래스" 역시 도입됩니다.
collections 모듈은 ABC로부터 파생된 몇 가지 구상(concrete) 클래스를 가지고 있습니다; 이것은 물론 더 파생될 수 있습니다. 또한, collections.abc 서브 모듈에는 클래스나 인스턴스가 특정 인터페이스를 (예를 들어, 해시 가능이거나 매핑이면) 제공하는지 검사하는 데 사용할 수 있는 ABC가 있습니다.
이 모듈은 ABC를 정의하기 위한 메타 클래스 ABCMeta와 상속을 통해 ABC를 정의하는 대안적 방법을 제공하는 도우미 클래스 ABC를 제공합니다:
-
class
abc.ABC¶ ABCMeta를 메타 클래스로 가지는 도우미 클래스. 이 클래스를 사용하면, 때로 혼란스러운 메타 클래스를 사용하지 않고, 추상 베이스 클래스를 간단히ABC에서 파생시켜서 만들 수 있습니다. 예를 들어:from abc import ABC class MyABC(ABC): pass
ABC의 형은 여전히ABCMeta이므로,ABC를 상속할 때는 메타 클래스 사용에 관한 일반적인 주의가 필요한데, 다중 상속이 메타 클래스 충돌을 일으킬 수 있기 때문입니다. metaclass 키워드를 전달하고ABCMeta를 직접 사용해서 추상 베이스 클래스를 정의할 수도 있습니다, 예를 들어:from abc import ABCMeta class MyABC(metaclass=ABCMeta): pass
flowdas
다중 상속이 메타 클래스 충돌을 일으킬 수 있다는 것은, 클래스를 정의할 때, 정의되는 클래스의 메타 클래스를 선택하는 규칙이 있고, 이 규칙을 만족하지 못하면
TypeError를 일으킨다는 뜻입니다. 이 규칙의 자세한 내용은 적절한 메타 클래스 선택하기 를 참조하세요.버전 3.4에 추가.
-
class
abc.ABCMeta¶ 추상 베이스 클래스 (ABC)를 정의하기 위한 메타 클래스.
이 메타 클래스를 사용하여 ABC를 만듭니다. ABC는 직접 서브 클래싱 될 수 있으며 믹스인 클래스의 역할을 합니다. 관련 없는 구상 클래스(심지어 내장 클래스도)와 관련 없는 ABC를 "가상 서브 클래스"로 등록 할 수 있습니다 -- 이들과 이들의 서브 클래스는 내장
issubclass()함수에 의해 등록하는 ABC의 서브 클래스로 간주합니다. 하지만 등록하는 ABC는 그들의 MRO (메서드 결정 순서)에 나타나지 않을 것이고, 등록하는 ABC가 정의한 메서드 구현도 호출할 수 없을 것입니다 (super()를 통해서도 가능하지 않습니다). 1flowdas
일부 ABC 는 구상 (즉 추상이 아닌) 메서드를 제공합니다. 가령
collections.abc에서 이런 ABC 들을 다수 정의하고 있습니다. 이런 ABC 는 믹스 인(mix-in) 클래스로 볼 수 있고, 이 구상 메서드들을 믹스 인 메서드라고 부르기도 합니다.ABCMeta를 메타 클래스로 생성된 클래스는 다음과 같은 메서드를 가집니다:-
register(subclass)¶ 이 ABC의 "가상 서브 클래스"로 subclass 를 등록합니다. 예를 들면:
from abc import ABC class MyABC(ABC): pass MyABC.register(tuple) assert issubclass(tuple, MyABC) assert isinstance((), MyABC)
버전 3.3에서 변경: 클래스 데코레이터로 사용할 수 있도록, 등록된 subclass 돌려줍니다.
버전 3.4에서 변경:
register()호출을 감지하려면,get_cache_token()함수를 사용할 수 있습니다.
추상 베이스 클래스에서 다음 메서드를 재정의할 수도 있습니다:
-
__subclasshook__(subclass)¶ (반드시 클래스 메서드로 정의되어야 합니다.)
subclass 를 이 ABC의 서브 클래스로 간주할지를 검사합니다. 이것은, ABC의 서브 클래스로 취급하고 싶은 클래스마다
register()를 호출할 필요 없이,issubclass의 행동을 더 사용자 정의할 수 있음을 의미합니다. (이 클래스 메서드는 ABC의__subclasscheck__()메서드에서 호출됩니다.)이 메서드는
True,False또는NotImplemented를 반환해야 합니다.True를 반환하면, subclass 를 이 ABC의 서브 클래스로 간주합니다.False를 반환하면, subclass 를 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추상 베이스 클래스의 일부이지만, 추상이 아닌 파생 클래스에서 재정의될 필요는 없습니다.flowdas
MyIterable이 정의하는__iter__()메서드는 빈 이터레이터(좀 더 구체적으로는 제너레이터)를 돌려주도록 구현되었습니다.yield문을 삽입하기위해while False로 감쌉니다. 실제 서브 클래스에서는 (여전히 빈 이터레이터를 돌려줄 것이라면 그대로 써도 되지만) 자신에 맞는__iter__()를 재구현해야하지만, (재정의된__iter__()를 호출하게될)get_iterator()메서드는 재정의할 필요없이 그대로 써도 좋다는 뜻입니다.여기에 정의된
__subclasshook__()클래스 메서드는 자신의 (또는 그것의__mro__리스트를 통해 액세스 되는 베이스 클래스 중 하나의)__dict__에__iter__()메서드를 가진 모든 클래스도MyIterable로 간주한다고 말합니다.flowdas
MyIterable의 핵심은get_iterator()를 제공하는 것입니다. 때문에MyIterable이 메서드가 제공가는 기본 구현이 동작하는데 필요한__iter__()가 정의되어있는지 검사하고 있습니다.마지막으로, 마지막 줄은,
Foo가__iter__()메서드를 정의하지는 않았음에도 불구하고 (이것은__len__()과__getitem__()로 정의되는 이전 스타일의 이터러블 프로토콜을 사용합니다),MyIterable의 가상 서브 클래스로 만듭니다. 이렇게 하면get_iterator가Foo의 메서드로 사용할 수 있지 않으므로, 별도로 제공됩니다.flowdas
하지만 이터레이터를 얻는데 꼭
__iter__()가 정의되어야 할 필요는 없고,__len__()과__getitem__()을 사용해도 됩니다. 그래서Foo역시MyIterable의 서브 클래스로 취급하기위해MyIterable.register(Foo)로 등록합니다. 하지만 이렇게 등록하는 경우Foo는MyIterable을 계승하지 않았기 때문에,MyIterable의 기본 구현get_iterator()를 제공받지 못합니다. 때문에Foo는get_iterator()를 직접 구현해서 사용자들이MyIterable에서 기대하는get_iterator()이 제공되도록 만듭니다.flowdas
any("__iter__" in B.__dict__ for B in C.__mro__)과 같은 구문은 자주 쓰이는 표현인데,hasattr(C, '__iter')에 비해 메타 클래스나 어트리뷰트 조회에 관련된 특수 메서드와 디스크립터의 개입을 회피하는 결과를 줍니다.-
abc 모듈은 다음 데코레이터도 제공합니다:
-
@abc.abstractmethod¶ 추상 메서드를 나타내는 데코레이터.
이 데코레이터를 사용하려면 클래스의 메타 클래스가
ABCMeta이거나 여기에서 파생된 것이어야 합니다.ABCMeta에서 파생된 메타 클래스를 가진 클래스는 모든 추상 메서드와 프로퍼티가 재정의되지 않는 한 인스턴스로 만들 수 없습니다. 추상 메서드는 일반적인 'super' 호출 메커니즘을 사용하여 호출 할 수 있습니다.abstractmethod()는 프로퍼티와 디스크립터에 대한 추상 메서드를 선언하는 데 사용될 수 있습니다.클래스에 추상 메서드를 동적으로 추가하거나, 메서드나 클래스가 작성된 후에 추상화 상태를 수정하려고 시도하는 것은 지원되지 않습니다.
abstractmethod()는 정규 상속을 사용하여 파생된 서브 클래스에만 영향을 줍니다; ABC의register()메서드로 등록된 "가상 서브 클래스" 는 영향을 받지 않습니다.abstractmethod()가 다른 메서드 디스크립터와 함께 적용될 때, 다음 사용 예제와 같이 가장 안쪽의 데코레이터로 적용되어야 합니다:class C(ABC): @abstractmethod def my_abstract_method(self, ...): ... @classmethod @abstractmethod def my_abstract_classmethod(cls, ...): ... @staticmethod @abstractmethod def my_abstract_staticmethod(...): ... @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)
추상 베이스 클래스 장치와 정확하게 상호 작용하기 위해서, 디스크립터는
__isabstractmethod__를 사용하여 자신을 추상으로 식별해야 합니다. 일반적으로 이 어트리뷰트는 디스크립터를 구성하는 데 사용된 메서드 중 어느 하나라도 추상이면True여야 합니다. 예를 들어, 파이썬의 내장property는 다음과 동등한 일을 합니다:class Descriptor: ... @property def __isabstractmethod__(self): return any(getattr(f, '__isabstractmethod__', False) for f in (self._fget, self._fset, self._fdel))
참고
자바 추상 메서드와 달리, 이 추상 메서드는 구현을 가질 수 있습니다. 이 구현은 그것을 재정의하는 클래스에서
super()메커니즘을 통해 호출 할 수 있습니다. 이는 협업적 다중 상속을 사용하는 프레임워크에서 super-호출의 종점으로 유용 할 수 있습니다.
abc 모듈은 다음 레거시 데코레이터도 지원합니다:
-
@abc.abstractclassmethod¶ 버전 3.2에 추가.
버전 3.3부터 폐지: 이제
classmethod와abstractmethod()를 함께 사용할 수 있어서, 이 데코레이터는 필요 없습니다.내장
classmethod()의 서브 클래스로, 추상 classmethod를 나타냅니다. 그 외에는abstractmethod()와 유사합니다.classmethod()데코레이터가 이제 추상 메서드에 적용될 때 추상으로 정확하게 식별되기 때문에, 이 특별한 경우는 폐지되었습니다.:class C(ABC): @classmethod @abstractmethod def my_abstract_classmethod(cls, ...): ...
-
@abc.abstractstaticmethod¶ 버전 3.2에 추가.
버전 3.3부터 폐지: 이제
staticmethod와abstractmethod()를 함께 사용할 수 있어서, 이 데코레이터는 필요 없습니다.내장
staticmethod()의 서브 클래스로, 추상 staticmethod를 나타냅니다. 그 외에는abstractmethod()와 유사합니다.staticmethod()데코레이터가 이제 추상 메서드에 적용될 때 추상으로 정확하게 식별되기 때문에, 이 특별한 경우는 폐지되었습니다.:class C(ABC): @staticmethod @abstractmethod def my_abstract_staticmethod(...): ...
-
@abc.abstractproperty¶ 버전 3.3부터 폐지: 이제
property,property.getter(),property.setter(),property.deleter()와abstractmethod()를 함께 사용할 수 있어서, 이 데코레이터는 필요 없습니다.내장
property()의 서브 클래스로, 추상 property를 나타냅니다.property()데코레이터가 이제 추상 메서드에 적용될 때 추상으로 정확하게 식별되기 때문에, 이 특별한 경우는 폐지되었습니다.: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 모듈은 또한 다음과 같은 기능을 제공합니다 :
-
abc.get_cache_token()¶ 현재의 추상 베이스 클래스 캐시 토큰을 반환합니다.
토큰은 가상 서브 클래스를 위한 추상 베이스 클래스 캐시의 현재 버전을 식별하는 (동등성 검사를 지원하는) 불투명 객체입니다. 임의의 ABC에서
ABCMeta.register()가 호출될 때마다 토큰이 변경됩니다.버전 3.4에 추가.
flowdas
이 함수는
issubclass()결과를 캐싱하는 경우(가령functools.singledispatch()), 가상 서브 클래스가 변경되었는지를 감지해서, 캐시를 무효화시키려고 할 때 사용됩니다.
각주
- 1
C++ 프로그래머는 파이썬의 가상 베이스 클래스 개념이 C++과 다르다는 것을 알아야 합니다.
