# 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 선언 시 런타임 상황에서 메서드가 실제로 호출이 되는 시점에서 에러 발생