# testFile.py 내용
from abc import ABCMeta, abstractmethod
class OriginClass(metaclass=ABCMeta):
@abstractmethod
def func1(self):
pass
# @abstractmethod
def func2(self):
pass
class SubClass(OriginClass):
def func1(self):
print("func1 구현"))
hello = SubClass()
hello.func1()
00 개요
- 목적: 파이썬에서 제공하는 abc 클래스의 개념 및 기능에 대해 정리하고자 함
01 abc 클래스란?
1. 정의
- 'Abstract Base Class'
- Python이 제공하는 '추상화 클래스'
- abc 클래스는 Base 클래스를 상속받는 파생 클래스가 반드시 Base 클래스의 Method를 명시적으로 선언해서 구현하도록 강제하는 추상화 클래스 기능
- 이 기능의 필요성을 이해하기 위해 상속과 다형성에 대한 이해 필요
1) 상속이란 (inheritance)
- OOP(객체 지향 프로그래밍)의 가장 강력한 기능 중 하나인 '상속'은 클래스의 재사용성을 높임으로서 코드의 반복에 따른 유지 보수 비용을 낮추는 데 큰 역할을 함
- 예시) Country 클래스의 속성을 상속받은 Korea 클래스
- 오버라이딩한 show() 메서드에서는 Korea 클래스에서 정의한 대로 출력되고 나머지의 경우 오버라이딩이 되지 않아서 부모 클래스에서 정의된 대로 출력하게 됨
# testFile.py 내용
class Country:
"""Super Class"""
name = '국가명'
poplation = '인구'
capital = '수도'
def show(self):
print('국가 클래스의 메소드입니다.')
class Korea(Country):
"""Sub class"""
def __init__(self, name):
self.name = name
def show(self):
print('국가 이름은: ', self.name)
>>> import testFile
>>> korea = testFile.Korea('대한민국')
>>> korea.show()
국가 이름은: 대한민국
>>> korea.capital
'수도'
>>> korea.name
'대한민국'
2) 다형성이란 (polymorphism)
- 하나의 인터페이스를 통해 서로 다른 여러 타입을 제공하는 것을 의미
- OOP에서의 다향성은 클래스에 선언된 메서드가 상속 받은 클래스에서 같은 이름으로 오버라이딩되어 여러 형태로 동작함을 의미
- 예시) OriginClass 정의 후 여기에 func1과 func2 메서드 선언
- 이러한 OirginClass를 상속받는 CopyClass1이 있다고 가정
- 이 클래스는 OriginClass의 func1과 func2를 각각 메소드 오버라이딩해서 구현됨
- 각각의 결과는 CopyClass1에서 정의한대로 출력됨
# testFile.py 내용
class OriginClass:
def func1(self):
pass
def func2(self):
pass
class CopyClass1(OriginClass):
def func1(self):
print("Func1 is inherited1")
def func2(self):
print("Func2 is inherited1")
copyclass1 = CopyClass1()
copyclass1.func1()
copyclass1.func2()
# 터미널에서
python testFile.py
# 출력값
Func1 is inherited1
Func2 is inherited1
- 예시) CopyClass2에서는 func1은 CopyClass1과 다르게 func2는 제외하고 구현됨
- func1의 결과는 CopyClass2에서 정의한대로 잘 출력되지만 func2의 결과는 아무것도 출력되지 않음
- 부모 클래스의 func2메서드에서 이를 pass만 하도록 구현한 상태이기 때문에 아무것도 하지 않고 넘어가는 것
- 이렇게 되면 나중에 이 부분의 구현을 추가해야한다는 사실을 잊고 넘어갈 수 있고, 추후 이로 인한 side effect가 생길 여지 있음
# testFile.py 내용
class OriginClass:
def func1(self):
pass
def func2(self):
pass
class CopyClass2(OriginClass):
def func1(self):
print("=========")
print("Func1 is inherited2")
print("=========")
copyclass2 = CopyClass2()
copyclass2.func1()
copyclass2.func2()
# 터미널에서
python testFile.py
# 출력값
=========
Func1 is inherited2
=========
- 위와 같은 불상사를 피하기 위해 아래와 같이 OriginClass에 에러 호출 부분 (NotImplementedError)을 추가
- 이렇게 구현 시 OriginClass를 상속받은 클래스에서 func2()를 구현하지 않고 호출한다면 자동으로 OriginClass의 메서드를 호출하고 NotImplementedError를 발생시킴
- 상속받은 클래스는 인스턴스화되지만 실제로 메서드를 실행시키는 단계에서 에러 발생
# testFile.py 내용
class OriginClass:
def func1(self):
raise NotImplementedError()
def func2(self):
raise NotImplementedError()
class CopyClass1(OriginClass):
def func1(self):
print("Func1 is inherited1")
def func2(self):
print("Func2 is inherited1")
class CopyClass2(OriginClass):
def func1(self):
print("=========")
print("Func1 is inherited2")
print("=========")
copyclass2 = CopyClass2()
print("CopyClass2().func1() being called: \n")
copyclass2.func1()
print("\nCopyClass2().func2() being called: \n")
copyclass2.func2()
- 위처럼 상속 클래스들을 관리할 수 있게 되고 추후 유지보수를 용이하게 할 수 있음
- 하지만 이것보다 더 strict한 방식을 제공하는 것이 abc 클래스!
3) abc (Abstract Base Class) 클래스란
- 추상 클래스는 메소드의 구체적인 구현이 없는 클래스
- 즉, 일종의 틀을 제공해주는 역할
- 이 틀은 추상 클래스를 상속하는 클래스들에게 특정 메소드의 구현을 강제하는 효과가 있게 함
- 어떻게? → abc를 부모 클래스에 적용 시 부모 클래스를 상속받는 모든 파생/자식/하위 클래스 (derived/child/sub class)에서 해당 메서드를 선언해서 구현하지 않으면 에러 발생
- Python의 추상 클래스는 Java의 인터페이스와 비슷한 개념
- 인터페이스란 일종의 포맷 스펙이며 형식을 강조함
- 즉, 일관된 인터페이스를 제공하기 위해 사용됨..?
- 예시) OrginClass에 abc를 적용
- 추상화시키고자 하는 메서드들에 데코레이터로 @abstractmethod를 선언하여 추상화 적용
- 적용 시 OriginClass를 상속받는 모든 파생 클래스 (sub class)에서 해당 메서드들을 선언해서 구현하지 않으면 에러 발생
# testFile.py 내용
from abc import ABCMeta, abstractmethod
class OriginClass(metaclass=ABCMeta):
@abstractmethod
def func1(self):
pass
@abstractmethod
def func2(self):
pass
class SubClass(OriginClass): # NOTE: fun1만 구현하고 fun2는 구현 안함
def func1(self):
print("func1 구현")
hello = SubClass()
hello.func1()
- ERROR 내용: func2 매서드 implement 안하고서는 SubClass()를 인스턴스화할 수 없음
- 즉, SubClass는 OriginClass를 상송받는데, OriginClass에서는 func1과 func2에 @abstractmethod가 적용되었기에 func1과 func2 둘 다 OriginClass를 상속받은 SubClass에서 구현되어야지만 SubClass()를 인스턴스화 가능한 것
- 해결 방법: 1) OriginClass를 상속받은 SubClass() 에서 func2를 구현, 또는 2) OriginClass에서 func2를 @abstractmethod 미적용
- 1) OriginClass를 상속받은 SubClass() 에서 func2를 구현
# testFile.py 내용
from abc import ABCMeta, abstractmethod
class OriginClass(metaclass=ABCMeta):
@abstractmethod
def func1(self):
pass
@abstractmethod
def func2(self):
pass
class SubClass(OriginClass):
def func1(self):
print("func1 구현")
def func2(self):
print("func2 구현")
hello = SubClass()
hello.func1()
hello.func2()
- 설명: Subclass()에서 func1과 func2 (OriginClass()에서 @abstractmethod 적용하여 추상화된 매소드들) 가 모두 구현되었기에 SubClass()가 hello라는 변수에 인스턴스화가 정상적으로 되었으며 hello 인스턴스로 해당 매소드(func1, func2)들이 정상적으로 호출되는 것 확인
- 2) OriginClass에서 func2를 @abstractmethod 미적용
# testFile.py 내용
from abc import ABCMeta, abstractmethod
class OriginClass(metaclass=ABCMeta):
@abstractmethod
def func1(self):
pass
# @abstractmethod 주석처리, 즉 추상화 미적용
def func2(self):
pass
class SubClass(OriginClass):
def func1(self):
print("func1 구현")
hello = SubClass()
hello.func1()
- 설명: OriginClass()에서 func2에는 @abstractmethod를 적용하지 않았기에 SubClass에서 func1만 구현해도 SubClass()가 hello라는 변수에 인스턴스화가 정상적으로 되며 hello 인스턴스로 해당 매소드(func1)가 정상적으로 호출되는 것 확인
4) abc 사용하는 것과 raise NotImplementedError를 메서드마다 선언해놓는 것의 차이
- 1> abc 클래스 사용 시 해당 OriginClass는 인스턴스화 될 수 없음; 단지 파생 클래스 구현을 위한 추상화 기능만 제공
- 2> abc 클래스 사용 시 에러 발생 시점이 다름
- NotImplementedError 선언 시 런타임 상황에서 메서드가 실제로 호출이 되는 시점에서 에러 발생
- abc 사용 시 인스턴스를 생성하는 것부터 에러 발생
참조
- https://amazelimi.tistory.com/entry/Python-%EC%B6%94%EC%83%81-%EB%A9%94%EC%84%9C%EB%93%9C-ABC%EC%9D%98-%EC%A0%95%EC%9D%98%EC%99%80-%EC%82%AC%EC%9A%A9
- https://xangmin.tistory.com/161
'Python' 카테고리의 다른 글
faulthandler (파이썬 추적 백업 모듈) (0) | 2024.07.05 |
---|---|
@classmethod, @staticmethod (파이썬 매서드 데코레이터) (1) | 2024.07.01 |
클래스, 객체, 속성, 메서드, 생성자, 인스턴스란 (파이썬 용어) (0) | 2024.06.29 |
distutils (파이썬 모듈 배포 패키지) (0) | 2024.06.18 |
ASGI란 (WSGI의 후계자) (0) | 2024.03.28 |