Python의 동적 특성과 덕 타이핑에 대한 지원은 유연성으로 인해 오랫동안 칭찬을 받아 왔습니다. 그러나 코드베이스가 더 커지고 복잡해짐에 따라 정적 유형 검사의 이점이 점점 더 분명해집니다. 하지만 덕 타이핑의 유연성과 정적 유형 검사의 안전성을 어떻게 조화시킬 수 있을까요? Python의 프로토콜 클래스를 입력하세요.
이 튜토리얼에서 배울 내용은 다음과 같습니다.
오리 타이핑(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 유형의 코드에서 일반적이지만 더 큰 코드베이스에서는 문제를 일으킬 수 있습니다.
오리 타이핑은 다음과 같은 몇 가지 장점을 제공합니다.
그러나 몇 가지 단점도 있습니다.
이러한 문제를 해결하는 한 가지 접근 방식은 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)
이 접근 방식은 더 나은 유형 검사와 더 명확한 인터페이스를 제공하지만 다음과 같은 단점이 있습니다.
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)
이를 분석해 보겠습니다.
이 접근 방식은 다음과 같은 몇 가지 이점을 제공합니다.
다음은 도메인 클래스(원형, 직사각형)를 평면으로 유지하면서 프로토콜이 필요한 만큼 복잡할 수 있는 방법(모양)을 보여주는 보다 복잡한 예입니다.
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의 프로토콜은 오리 유형의 코드에 정적 유형을 적용하는 강력한 방법을 제공합니다. 이를 통해 상속을 요구하지 않고 유형 시스템에서 인터페이스를 지정할 수 있으며 덕 타이핑의 유연성을 유지하면서 정적 유형 검사의 이점을 추가할 수 있습니다.
프로토콜을 사용하면 다음을 수행할 수 있습니다.
Python의 프로토콜 및 유형 힌트에 대해 자세히 알아보려면 타이핑 모듈에 대한 공식 Python 문서를 확인하거나 mypy와 같은 고급 정적 유형 검사 도구를 살펴보세요.
즐거운 코딩을 하시길 바랍니다. 여러분의 오리가 항상 유형 안전을 지키길 바랍니다!
여기에서 내 뉴스레터를 포함한 더 많은 콘텐츠를 찾을 수 있습니다.
부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.
Copyright© 2022 湘ICP备2022001581号-3