"일꾼이 일을 잘하려면 먼저 도구를 갈고 닦아야 한다." - 공자, 『논어』.
첫 장 > 프로그램 작성 > 오리 타이핑과 유형 힌트의 만남: Python에서 프로토콜 사용

오리 타이핑과 유형 힌트의 만남: Python에서 프로토콜 사용

2024-09-04에 게시됨
검색:354

Duck Typing Meets Type Hints: Using Protocols in Python

Python의 동적 특성과 덕 타이핑에 대한 지원은 유연성으로 인해 오랫동안 칭찬을 받아 왔습니다. 그러나 코드베이스가 더 커지고 복잡해짐에 따라 정적 유형 검사의 이점이 점점 더 분명해집니다. 하지만 덕 타이핑의 유연성과 정적 유형 검사의 안전성을 어떻게 조화시킬 수 있을까요? Python의 프로토콜 클래스를 입력하세요.

이 튜토리얼에서 배울 내용은 다음과 같습니다.

  1. 덕 타이핑(Duck Typing)이란 무엇이며 Python에서 지원되는 방식
  2. 덕 타이핑의 장점과 단점
  3. 추상 기본 클래스(ABC)가 입력 문제를 해결하는 방법
  4. 프로토콜을 사용하여 두 가지 장점을 모두 활용하는 방법: 정적 유형 검사를 통한 덕 타이핑 유연성

오리 타이핑 이해

오리 타이핑(Duck Typing)은 개체의 유형이나 클래스가 개체가 정의하는 메서드보다 덜 중요한 프로그래밍 개념입니다. '오리처럼 생기고, 오리처럼 헤엄치고, 오리처럼 꽥꽥거린다면 아마도 오리일 것이다'라는 생각에서 출발한 것입니다.

Python에서는 덕 타이핑이 완벽하게 지원됩니다. 예를 들어:

class Duck:
    def quack(self):
        print("Quack!")

class Person:
    def quack(self):
        print("I'm imitating a duck!")

def make_it_quack(thing):  # Note: No type hint here
    thing.quack()

duck = Duck()
person = Person()

make_it_quack(duck)    # Output: Quack!
make_it_quack(person)  # Output: I'm imitating a duck!

이 예에서 make_it_quack는 사물의 유형에 관심이 없습니다. 그 물건에 돌팔이 방법이 있는지에만 관심이 있습니다. thing 매개변수에는 유형 힌트가 없습니다. 이는 duck 유형의 코드에서 일반적이지만 더 큰 코드베이스에서는 문제를 일으킬 수 있습니다.

오리 타이핑의 장점과 단점

오리 타이핑은 다음과 같은 몇 가지 장점을 제공합니다.

  1. 유연성: 특정 유형에 얽매이지 않는 보다 유연한 코드를 허용합니다.
  2. 더 쉬워진 코드 재사용: 수정 없이 새로운 컨텍스트에서 기존 클래스를 사용할 수 있습니다.
  3. 행동 강조: 객체가 무엇인지보다는 객체가 무엇을 할 수 있는지에 중점을 둡니다.

그러나 몇 가지 단점도 있습니다.

  1. 명확성 부족: 객체가 어떤 메서드를 구현해야 하는지 불분명할 수 있습니다.
  2. 런타임 오류: 유형 관련 오류는 런타임에만 포착됩니다.
  3. 적은 IDE 지원: IDE는 정확한 자동 완성 및 오류 검사를 제공하는 데 어려움을 겪고 있습니다.

ABC 솔루션

이러한 문제를 해결하는 한 가지 접근 방식은 ABC(추상 기본 클래스)를 사용하는 것입니다. 예는 다음과 같습니다.

from abc import ABC, abstractmethod

class Quacker(ABC):
    @abstractmethod
    def quack(self):
        pass

class Duck(Quacker):
    def quack(self):
        print("Quack!")

class Person(Quacker):
    def quack(self):
        print("I'm imitating a duck!")

def make_it_quack(thing: Quacker):
    thing.quack()

duck = Duck()
person = Person()

make_it_quack(duck)
make_it_quack(person)

이 접근 방식은 더 나은 유형 검사와 더 명확한 인터페이스를 제공하지만 다음과 같은 단점이 있습니다.

  1. 상속이 필요하며 이는 유연성 없는 계층 구조로 이어질 수 있습니다.
  2. 수정할 수 없는 기존 클래스에서는 작동하지 않습니다.
  3. 이는 Python의 "오리 타이핑" 철학에 위배됩니다.

프로토콜: 양쪽 세계의 최고

Python 3.8에는 상속 없이 인터페이스를 정의할 수 있는 프로토콜 클래스가 도입되었습니다. 사용 방법은 다음과 같습니다.

from typing import Protocol

class Quacker(Protocol):
    def quack(self):...

class Duck:
    def quack(self):
        print("Quack!")

class Person:
    def quack(self):
        print("I'm imitating a duck!")

def make_it_quack(thing: Quacker):
    thing.quack()

duck = Duck()
person = Person()

make_it_quack(duck)
make_it_quack(person)

이를 분석해 보겠습니다.

  1. 우리는 우리가 기대하는 인터페이스를 지정하는 Quacker 프로토콜을 정의합니다.
  2. Duck 및 Person 클래스는 어떤 것에서도 상속할 필요가 없습니다.
  3. make_it_quack와 함께 유형 힌트를 사용하여 Quacker가 예상된다는 것을 지정할 수 있습니다.

이 접근 방식은 다음과 같은 몇 가지 이점을 제공합니다.

  1. 정적 유형 검사: IDE 및 유형 검사기는 런타임 전에 오류를 포착할 수 있습니다.
  2. 상속이 필요하지 않습니다. 기존 클래스는 올바른 메서드가 있는 한 작동합니다.
  3. 명확한 인터페이스: 프로토콜은 어떤 방법이 예상되는지 명확하게 정의합니다.

다음은 도메인 클래스(원형, 직사각형)를 평면으로 유지하면서 프로토콜이 필요한 만큼 복잡할 수 있는 방법(모양)을 보여주는 보다 복잡한 예입니다.

from typing import Protocol, List

class Drawable(Protocol):
    def draw(self): ...

class Resizable(Protocol):
    def resize(self, factor: float): ...

class Shape(Drawable, Resizable, Protocol):
    pass

def process_shapes(shapes: List[Shape]):
    for shape in shapes:
        shape.draw()
        shape.resize(2.0)

# Example usage
class Circle:
    def draw(self):
        print("Drawing a circle")

    def resize(self, factor: float):
        print(f"Resizing circle by factor {factor}")

class Rectangle:
    def draw(self):
        print("Drawing a rectangle")

    def resize(self, factor: float):
        print(f"Resizing rectangle by factor {factor}")

# This works with any class that has draw and resize methods,
# regardless of its actual type or inheritance
shapes: List[Shape] = [Circle(), Rectangle()]
process_shapes(shapes)

이 예에서 Circle 및 Rectangle은 Shape 또는 다른 클래스에서 상속되지 않습니다. 그들은 단순히 필요한 메소드(그리기 및 크기 조정)를 구현합니다. process_shapes 함수는 Shape 프로토콜 덕분에 이러한 메소드를 가진 모든 객체와 함께 작동할 수 있습니다.

요약

Python의 프로토콜은 오리 유형의 코드에 정적 유형을 적용하는 강력한 방법을 제공합니다. 이를 통해 상속을 요구하지 않고 유형 시스템에서 인터페이스를 지정할 수 있으며 덕 타이핑의 유연성을 유지하면서 정적 유형 검사의 이점을 추가할 수 있습니다.

프로토콜을 사용하면 다음을 수행할 수 있습니다.

  1. 코드에 대한 명확한 인터페이스 정의
  2. 더 나은 IDE(정적 유형 검사), 지원 및 오류 조기 발견
  3. 덕 타이핑의 유연성 유지
  4. 수정할 수 없는 클래스에 대한 유형 검사를 활용하세요.

Python의 프로토콜 및 유형 힌트에 대해 자세히 알아보려면 타이핑 모듈에 대한 공식 Python 문서를 확인하거나 mypy와 같은 고급 정적 유형 검사 도구를 살펴보세요.

즐거운 코딩을 하시길 바랍니다. 여러분의 오리가 항상 유형 안전을 지키길 바랍니다!

여기에서 내 뉴스레터를 포함한 더 많은 콘텐츠를 찾을 수 있습니다.

릴리스 선언문 이 기사는 https://dev.to/samkeen/duck-typing-meets-type-hints-using-protocols-in-python-4d8l?1에 복제되어 있습니다. 침해가 있는 경우에는 [email protected]으로 문의해 주세요. 그것을 삭제하려면
최신 튜토리얼 더>

부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.

Copyright© 2022 湘ICP备2022001581号-3