콘텐츠로 건너뛰기

파이썬 인터페이스 사용 방법 설명: 손쉽게(interface in python)

[

Python에서 인터페이스 구현하기

인터페이스는 소프트웨어 엔지니어링에서 중요한 역할을 합니다. 애플리케이션이 성장함에 따라 코드 베이스의 업데이트와 변경은 더욱 어려워집니다. 종종 비슷해 보이지만 관련이 없는 클래스가 생기게 되어 혼란을 초래할 수 있습니다. 이 튜토리얼에서는 현재 문제를 해결하기 위해 어떤 클래스를 사용해야 하는지를 결정하는 데 도움이 되는 Python 인터페이스를 사용하는 방법을 알아보겠습니다.

이 튜토리얼에서 다음을 할 수 있습니다:

  • 인터페이스를 사용하는 방법과 Python 인터페이스 생성의 주의점을 이해합니다.
  • Python과 같은 동적 언어에서 인터페이스의 유용성을 이해합니다.
  • 비공식적인 Python 인터페이스를 구현하는 방법을 배웁니다.
  • abc.ABCMeta@abc.abstractmethod를 사용하여 공식적인 Python 인터페이스를 사용하는 방법을 알아봅니다.

Python에서 인터페이스는 대부분 다른 언어와 다르게 처리됩니다. 디자인 복잡성에 따라 다양한 방식으로 다룰 수 있습니다. 이 튜토리얼을 마칠 때쯤이면 Python의 데이터 모델에 대해 더 잘 이해할 수 있으며, Java, C++, Go와 같은 언어의 인터페이스와 Python의 인터페이스를 비교할 수 있을 것입니다.

퀴즈 풀기: 대화식 “Python에서 인터페이스 구현하기” 퀴즈로 지식을 테스트해보세요. 완료하면 학습 진행도를 추적할 수 있는 점수를 받게 됩니다.

퀴즈 풀기 »

Python 인터페이스 개요

인터페이스는 클래스를 디자인하기 위한 블루프린트 역할을 합니다. 클래스와 마찬가지로 인터페이스도 메서드를 정의합니다. 그러나 클래스와는 다르게 이러한 메서드는 추상적입니다. 추상 메서드는 인터페이스에서 단순히 정의된 메서드로, 구현되지 않습니다. 대신 인터페이스를 구현하는 클래스가 추상 메서드에 구체적인 의미를 부여합니다.

비공식적인 인터페이스

특정 상황에서는 공식적인 Python 인터페이스의 엄격한 규칙이 필요하지 않을 수 있습니다. Python의 동적 특성을 활용하여 비공식적인 인터페이스를 구현할 수 있습니다. 비공식적인 Python 인터페이스는 메서드를 정의하는 클래스로, 재정의할 수 있지만 엄격한 강제성은 없습니다.

class InformalParserInterface:
def load_data_source(self, path: str, file_name: str) -> str:
"""Load in the file for extracting text."""
pass
def extract_text(self, full_file_name: str) -> dict:
"""Extract text from the currently loaded file."""
pass

InformalParserInterface.load_data_source().extract_text() 두 가지 메서드를 정의합니다. 이러한 메서드는 추상적입니다.

샘플 코드 1: Informal Parser Interface

class InformalParserInterface:
def load_data_source(self, path: str, file_name: str) -> str:
"""Load in the file for extracting text."""
pass
def extract_text(self, full_file_name: str) -> dict:
"""Extract text from the currently loaded file."""
pass

이 인터페이스는 PdfParserEmlParser 클래스에서 구현됩니다. 이 클래스들은 InformalParserInterface를 상속받아 load_data_source()extract_text() 메서드를 구현합니다.

샘플 코드 2: Informal 인터페이스를 구현하는 클래스

class PdfParser(InformalParserInterface):
def load_data_source(self, path: str, file_name: str) -> str:
"""PDF 파일을 로드합니다."""
pass
def extract_text(self, full_file_name: str) -> dict:
"""PDF 파일에서 텍스트를 추출합니다."""
pass
class EmlParser(InformalParserInterface):
def load_data_source(self, path: str, file_name: str) -> str:
"""EML 파일을 로드합니다."""
pass
def extract_text(self, full_file_name: str) -> dict:
"""EML 파일에서 텍스트를 추출합니다."""
pass

이렇게 비공식적인 인터페이스를 구현하면, PdfParserEmlParser 클래스에서는 각각의 파일 형식에 맞게 load_data_source()extract_text() 메서드를 구현할 수 있게 됩니다.

이와 같은 방식으로 비공식적인 인터페이스를 구현하면 유연성과 확장성을 얻을 수 있습니다.

공식적인 인터페이스

일부 상황에서는 비공식적인 인터페이스의 유연성보다는 더 엄격한 규칙이 필요할 수 있습니다. 이때 공식적인 Python 인터페이스를 구현할 수 있습니다. 공식적인 인터페이스는 abc 모듈을 사용하여 구현할 수 있으며, 강제적으로 추상 메서드를 정의하여 클래스에서 구현될 수 있도록 합니다.

abc.ABCMeta를 사용하는 방법

가장 간단한 방법은 abc.ABCMeta 메타클래스를 사용하는 것입니다. 이는 클래스에 abc.ABC를 상속함으로써 구현됩니다. abc.ABCMeta를 사용하면 클래스를 추상 클래스로 만들 수 있으며, 추상 메서드를 통해 인터페이스를 선언할 수 있습니다.

샘플 코드 3: ABCMeta 메타클래스를 사용한 공식적인 인터페이스

from abc import ABCMeta, abstractmethod
class FormalParserInterface(metaclass=ABCMeta):
@abstractmethod
def load_data_source(self, path: str, file_name: str) -> str:
"""인터페이스에서 추상 메서드로 정의된 데이터 소스 로드."""
pass
@abstractmethod
def extract_text(self, full_file_name: str) -> dict:
"""인터페이스에서 추상 메서드로 정의된 텍스트 추출."""
pass

FormalParserInterface 클래스는 abc.ABCMeta 메타클래스를 이용해 만들어진 추상 클래스입니다. 이 추상 클래스는 load_data_source()extract_text() 메서드를 추상 메서드로 정의합니다.

.__subclasshook__()을 사용하는 방법

또 다른 방법은 클래스에 .__subclasshook__() 메서드를 정의하여 인터페이스를 구현할 수 있는지 확인하는 것입니다. 이 방법은 인터페이스를 구현하는 클래스가 실제로 추상 메서드를 구현하는지 확인할 수 있는 방법입니다.

샘플 코드 4: .__subclasshook__()을 사용한 공식적인 인터페이스

from abc import ABC, ABCMeta, abstractmethod
class FormalParserInterface(metaclass=ABCMeta):
@classmethod
def __subclasshook__(cls, subclass):
return (
hasattr(subclass, 'load_data_source') and callable(subclass.load_data_source) and
hasattr(subclass, 'extract_text') and callable(subclass.extract_text)
)
class PdfParserInterface(FormalParserInterface):
def load_data_source(self, path: str, file_name: str) -> str:
"""PDF 파일을 로드합니다."""
pass
def extract_text(self, full_file_name: str) -> dict:
"""PDF 파일에서 텍스트를 추출합니다."""
pass
class EmlParserInterface(FormalParserInterface):
def load_data_source(self, path: str, file_name: str) -> str:
"""EML 파일을 로드합니다."""
pass
def extract_text(self, full_file_name: str) -> dict:
"""EML 파일에서 텍스트를 추출합니다."""
pass

FormalParserInterface 클래스에는 .__subclasshook__() 메서드가 정의되어 있습니다. 이 메서드는 인터페이스를 구현하는 클래스의 조건을 확인하고, 추상 메서드인 load_data_source()extract_text()를 구현하는 경우 True를 반환합니다.

abc를 통해 가상 서브클래스를 등록하는 방법

abc 모듈을 사용하면 인터페이스를 구현하는 가상 서브클래스를 등록할 수 있습니다. 이를 통해 인터페이스의 일부 메서드만 구현하고 나머지 메서드는 추상 메서드로 남겨둘 수 있습니다.

샘플 코드 5: 가상 서브클래스 등록을 통한 공식적인 인터페이스

from abc import ABC, abstractmethod
class FormalParserInterface(ABC):
@abstractmethod
def load_data_source(self, path: str, file_name: str) -> str:
"""데이터 소스 로드."""
pass
@abstractmethod
def extract_text(self, full_file_name: str) -> dict:
"""텍스트 추출."""
pass
FormalParserInterface.register(PdfParser)
FormalParserInterface.register(EmlParser)

FormalParserInterface 클래스에는 register() 메서드를 사용하여 PdfParserEmlParser 클래스를 가상 서브클래스로 등록합니다.

등록을 통한 서브클래스 검출 방법

인터페이스를 구현하는 클래스를 검출하는 또 다른 방법은 등록을 통해 가상 서브클래스를 찾는 것입니다. 이를 통해 인터페이스 타입을 가진 객체를 검사할 수 있습니다.

샘플 코드 6: 등록을 통한 서브클래스 검출

from abc import ABC, abstractmethod
class FormalParserInterface(ABC):
@abstractmethod
def load_data_source(self, path: str, file_name: str) -> str:
"""데이터 소스 로드."""
pass
@abstractmethod
def extract_text(self, full_file_name: str) -> dict:
"""텍스트 추출."""
pass
class PdfParser(FormalParserInterface):
def load_data_source(self, path: str, file_name: str) -> str:
"""PDF 파일을 로드합니다."""
pass
def extract_text(self, full_file_name: str) -> dict:
"""PDF 파일에서 텍스트를 추출합니다."""
pass
class EmlParser(FormalParserInterface):
def load_data_source(self, path: str, file_name: str) -> str:
"""EML 파일을 로드합니다."""
pass
def extract_text(self, full_file_name: str) -> dict:
"""EML 파일에서 텍스트를 추출합니다."""
pass
class OtherParser:
def load_data_source(self, path: str, file_name: str) -> str:
"""다른 파일을 로드합니다."""
pass
def extract_text(self, full_file_name: str) -> dict:
"""다른 파일에서 텍스트를 추출합니다."""
pass
print(issubclass(PdfParser, FormalParserInterface)) # True
print(issubclass(EmlParser, FormalParserInterface)) # True
print(issubclass(OtherParser, FormalParserInterface)) # False

위와 같이 등록을 통해 서브클래스를 검출하면 PdfParserEmlParser 클래스는 FormalParserInterface를 구현하는 서브클래스로 인정되지만, OtherParser 클래스는 그렇지 않습니다.

추상 메서드 선언을 통한 방법

이 방법은 abc.ABC를 상속한 클래스에서 메서드를 정의하여 인터페이스를 구현할 수 있도록 하는 것입니다. 이를 통해 클라이언트 코드에서 인터페이스의 구조에 맞게 메서드를 구현할 수 있습니다.

샘플 코드 7: 추상 메서드를 이용한 공식적인 인터페이스

from abc import ABC, abstractmethod
class FormalParserInterface(ABC):
@abstractmethod
def load_data_source(self, path: str, file_name: str) -> str:
"""데이터 소스 로드."""
pass
@abstractmethod
def extract_text(self, full_file_name: str) -> dict:
"""텍스트 추출."""
pass
class PdfParserInterface(FormalParserInterface):
def load_data_source(self, path: str, file_name: str) -> str:
"""PDF 파일을 로드합니다."""
pass
def extract_text(self, full_file_name: str) -> dict:
"""PDF 파일에서 텍스트를 추출합니다."""
pass
class EmlParserInterface(FormalParserInterface):
pass

FormalParserInterface 클래스는 ABC 클래스를 상속하여 추상 클래스로 만듭니다. 추상 메서드인 load_data_source()extract_text()를 정의하고, PdfParserInterfaceEmlParserInterface 클래스에서 이 추상 메서드를 구현합니다.

이렇게 하면 PDFParserInterface 클래스에서는 추상 메서드를 모두 구현하지만, EmlParserInterface에서는 필요한 경우 추상 메서드를 구현하지 않을 수 있습니다.

다른 언어에서의 인터페이스

Python 외의 다른 언어에서는 인터페이스의 구현 방식이 다를 수 있습니다. 여기에는 Java, C++, Go 등이 포함됩니다. Java와 C++은 interface 키워드를 사용하여 인터페이스를 정의하고 구현합니다. Go는 인터페이스를 명시적으로 정의하지 않지만, 사용자 정의 타입이 인터페이스를 구현하면 해당 타입을 인터페이스로 사용할 수 있습니다.

Python은 동적 언어인 만큼 인터페이스 구현에 더 유연한 방식을 제공합니다. 이를 통해 클래스간 상호작용을 쉽게 구현할 수 있으며, 코드의 재사용성과 유지보수성을 높일 수 있습니다.

결론

인터페이스는 Python에서 다른 언어와 다르게 다루어집니다. Python은 인터페이스에 특정 키워드를 사용하지 않으며, 인터페이스를 구현하는 클래스가 모든 추상 메서드를 정의할 필요가 없습니다. 비공식적인 인터페이스와 공식적인 인터페이스의 구현 방법을 살펴보았으며, 이를 통해 클래스 간의 상호작용을 관리하고 유지보수할 수 있는 방법을 배웠습니다.

이러한 인터페이스 개념은 더 복잡한 애플리케이션에서 매우 중요한 역할을 합니다. 적절한 인터페이스를 사용하면 코드의 유지보수성을 높일 수 있으며, 클래스 간의 관계를 명확하게 정의할 수 있습니다. Python에서 인터페이스를 활용하면 간편하고 효과적인 코드 작성이 가능하며, 다른 언어의 인터페이스와 비교하여 Python의 데이터 모델에 대한 더 깊은 이해를 얻을 수 있습니다.

(원문 출처 및 원작자 정보를 언급하지 마세요.)