Python 프로토콜 살펴보기(PEP 544)

Python 3.5, PEP 484에 추가된 유형 힌트가 정말 마음에 들지만 PEP 544 - Protocols: Structural subtyping(static duck typing)이 완전히 놓쳤습니다. 프로토콜은 Python 3.8에 추가되었으며 현재 Python의 안정적인 버전이지만 거의 1년이 되었습니다. 우리는 Python 3.9에 거의 근접하고 있으며 출시는 10월 5일로 예정되어 있습니다. 이 게시물에서는 먼저 프로토콜을 살펴보고 VS Code에서 Pylance를 사용하여 linting과 함께 작동하는 방법을 살펴보겠습니다.

PEP의 제목은 실제로 프로토콜에 대한 정말 좋은 설명인 정적 오리 타이핑을 말합니다. 이를 통해 객체가 수행해야 하는 인터페이스/템플릿을 정의할 수 있으며, 수행하는 경우 프로토콜의 인스턴스라고 합니다. 프로토콜은 유형 검사 도구에 의해 확인되며 런타임에 적용되지 않습니다.

sidenote: 저와 같이 TypesScript(TS)를 가지고 노는 사람들에게 프로토콜은 TS의 인터페이스와 정말 비슷합니다.

VS Code의 유형 검사 프로토콜



프로토콜을 이해하는 가장 쉬운 방법은 코드를 작성하는 것이므로 본격적으로 살펴보겠습니다. 먼저 프로토콜Printable을 정의합니다.

from abc import abstractmethod
from typing import Protocol
class Printable(Protocol):
    @abstractmethod
    def print(self) -> None:
        raise NotImplementedError


보시다시피 이것은 class Protocol 에서 상속받은 클래스입니다. 여기에는 인수를 받지 않고 None을 반환하는 단일 추상 메서드print가 있습니다. 함수에 return 문이 없으면 PythonNone이 반환됩니다.

이 프로토콜을 충족하는 클래스와 그렇지 않은 클래스를 정의해 봅시다.

class MyPrintable:
    def print(self) -> None:
        print("Hello DEV!")

class NonPrintable:
    pass


Pylance 플러그인과 함께 Visual Studio Code를 사용하고 있습니다. Pylance에서는 설정python.analysis.typeCheckingMode을 사용할 수 있습니다. 해제, 기본 또는 엄격일 수 있습니다. 현재 기본으로 실행중입니다. 이 게시물의 스크린샷은 이러한 설정을 사용합니다.

이제 Printable를 취하는 함수를 정의하고 편집기가 방금 생성된 두 클래스에 어떻게 반응하는지 살펴보겠습니다.

def simple_print(printable: Printable):
    printable.print()


MyPrintable 인스턴스를 전달한 다음 NonPrintable 인스턴스를 함수에 전달하면 이것이 편집기에 표시됩니다.



NonPrintable에 밑줄이 그어져 있으며 가리키면 다음 오류 메시지를 볼 수 있습니다.



Mypy와 Pylance는 모두 NonPrintable이 Printable 프로토콜을 이행하지 않는 방식에 대해 불평합니다. 이것은 또한 MyPrintable 개체가 프로토콜을 충족하는 것으로 올바르게 식별되며 상속이 필요하지 않음을 의미합니다.

sidenote: 저는 Pylance를 정말 즐기고 있습니다. 저 오류 메시지를 보세요! 환상적입니다!

앞서 언급한 것처럼 런타임 시 이 정보는 사용되지 않습니다. 그러나 NonPrintable에서 print-method가 존재하지 않기 때문에 호출하려고 하면 당연히 프로그램이 충돌합니다.

런타임 유형 검사



파이썬에서는 객체가 클래스의 인스턴스인지 확인하기 위해 일반적으로 isinstance 함수를 사용합니다. 이것은 프로토콜에서도 작동합니까? 나는 우리가 그것을 시도해야 한다고 생각하고 그렇게 하기 위해 다음과 같이 썼습니다.

def my_print(printable: Printable):
    if isinstance(printable, Printable):
        print("It is a printable!")
        printable.print()
    else:
        print("boooo, not a printable")

my_print(MyPrintable())
my_print(NonPrintable())


이제 Pylance가 이것에 대해 어떻게 생각하는지 봅시다.



글쎄요, 별로 좋아보이진 않네요.



좋아요, 프로토콜이 isinstance 검사에서 작동하지 않습니다. 다른 오류 메시지도 살펴보겠습니다.



흥미롭게도 isinstance를 사용하여 프로토콜에 대해 인스턴스를 확인하려면 프로토콜을 @runtime_checkable로 장식해야 합니다. 코드를 실행하는 경우에도 이에 대한 정보를 받게 됩니다. 런타임에 이 오류를 보고합니다.

TypeError: Instance and class checks can only be used with @runtime_checkable protocols


오류에 따르면 다음과 같이 데코레이터를 클래스에 추가하기만 하면 됩니다.

@runtime_checkable 
class Printable(Protocol):
    @abstractmethod
    def print(self) -> None:
        raise NotImplementedError


이제 동일한 코드를 실행하면 다음 출력이 표시됩니다.

It is a printable!
Hello DEV!
boooo, not a printable


최종 참고 사항



PEP544는 함수 또는 클래스가 매개 변수에 대해 갖는 요구 사항을 정의할 수 있는 프로토콜을 제공합니다. 더욱 흥미롭게도 isinstance 로 작업하는 동안 상속을 사용하지 않고 이 작업을 수행할 수 있습니다.

이것은 프로토콜에 대한 첫 번째 보기에 불과했으며 더 많은 것을 탐색할 수 있습니다. 예를 들어 인스턴스 및 클래스 변수 정의를 지원합니다. 프로토콜은 일반적이고 확장되고 병합될 수도 있습니다. 이것은 유형 검사 Python을 작성하기 위한 정말 흥미로운 도구를 추가하고 향후 프로젝트에서 이를 실험하는 것이 재미있을 것입니다.

좋은 웹페이지 즐겨찾기