C.W.K.
Stream
Lesson 07 of 07 · published

Protocol vs ABC — 구조 타이핑과 명목 타이핑

~20 min · protocol, abc, duck-typing, structural, nominal

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

"이 모양 가지고 있다" 표현하는 두 방법

가끔 "이 함수는 열고 닫을 수 있는 거 받는다" 라고 말하고 싶어. 클래스 계층 신경 X — 어떤 메서드가 있는지 신경. Python 이 두 정식 방법 — typing.Protocol (구조 타이핑 — 모양으로 매치) 와 abc.ABC (명목 타이핑 — 상속으로 매치).

Duck typing — 비공식 버전

"이게 어떤 타입이어야 하나" 에 대한 원래 Python 답변 — 체크 X, 그냥 시도. *오리처럼 걷고 오리처럼 꽥꽥거리면 오리*. 런타임 동작엔 작동, 근데 정적 타입 체커한테는 아무것도 안 알려. 현대 Python 은 런타임 duck typing 유지 + 타입 레벨에 구조 protocol 추가.

Protocol — 구조 타이핑

typing.Protocol 상속하는 클래스 정의, 필요한 메서드/속성. 그 메서드 가진 모든 클래스 (호환 시그니처) 는 자동으로 서브타입 — 상속 불필요. 정적 타입 체커 (mypy) 가 검증. 런타임 isinstance 는 protocol 에 @runtime_checkable 표시 시만, 그것도 메서드 존재만 체크 (시그니처 X).

ABC — 추상 메서드 + 명목 타이핑

abc.ABC 상속 + 메서드에 @abstractmethod 표시. 서브클래스가 그 메서드들 구현해야 — 안 하면 인스턴스화 X. ABC 는 상속 통해 계약 강제 — 클래스가 서브타입이라고 명시적으로 선언해야. Protocol 보다 무거움, 진짜 계층 + 공유 베이스 구현 원할 때 적절.

원칙: "이 메서드들 가진 거 뭐든" 엔 Protocol — 유연, 상속 부담 X. 진짜로 클래스 계층 + 공유 베이스 동작 + 명시적 선언 원하면 ABC. Pippa 의 backend/adapters/base.pyAdapter 베이스가 ABC 인 이유 — 헬퍼 메서드 공유 + 진짜 계층 표현.

Code

Duck typing — 원래 방식·python
def total_chars(thing):
    # 체크 X — 그냥 호출. 글자 줄 방법 있으면 작동.
    return sum(1 for _ in thing)

print(total_chars("hello"))      # 5
print(total_chars([1, 2, 3]))    # 3
print(total_chars(("a", "b")))   # 2
Protocol — 구조 타이핑·python
from typing import Protocol, runtime_checkable

@runtime_checkable
class Closable(Protocol):
    def close(self) -> None: ...

# close() 메서드 있는 모든 게 Closable
class File:
    def close(self): print("파일 닫힘")

class Connection:
    def close(self): print("연결 닫힘")

class NotClosable:
    pass

def shutdown(item: Closable) -> None:
    item.close()

shutdown(File())                  # 작동 — File 에 close 있음
shutdown(Connection())            # 작동 — Connection 에 close 있음

# 런타임 체크
print(isinstance(File(), Closable))         # True
print(isinstance(NotClosable(), Closable))  # False

# File / Connection 이 Closable 상속 X.
# 구조 매치 — 명목 X.
ABC — 추상 메서드 + 명목 타이핑·python
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self) -> float:
        ...

    def describe(self):                 # 구체 메서드, 모든 서브클래스 공유
        return f"{type(self).__name__} 면적 {self.area()}"

class Square(Shape):
    def __init__(self, side):
        self.side = side

    def area(self):
        return self.side ** 2

print(Square(5).describe())          # 'Square 면적 25'

# 추상 베이스 인스턴스화 X
try:
    Shape()
except TypeError as e:
    print(e)                          # Can't instantiate abstract class Shape

# area 구현 안 한 서브클래스도 인스턴스화 X
class Broken(Shape):
    pass

try:
    Broken()
except TypeError as e:
    print(e)
선택 — Protocol 또는 ABC?·python
# Protocol 사용 시기 —
# - 유연성 원함 (소유 X 인 third-party 클래스도 매치 가능)
# - 공유 구현 X, 모양 계약만
from typing import Protocol

class Renderable(Protocol):
    def render(self) -> str: ...

# .render() 가진 모든 클래스가 Renderable — 못 바꾸는 라이브러리 클래스 포함.

# ABC 사용 시기 —
# - 명시적 서브클래스 선언 원함 (서브클래스가 *반드시* 상속)
# - 상속할 공유 베이스 동작 있음
from abc import ABC, abstractmethod

class Brain(ABC):
    @abstractmethod
    async def stream(self, prompt): ...

    def heal_session(self):    # 공유 구체 메서드
        # ... 세션 힐 ...
        pass

# Pippa 에서 Adapter 가 ABC 인 이유 — 4 brain 이 streaming
# 계약 + healing 로직 공유, 계층이 의도적.

External links

Exercise

Protocol Comparable 정의 — 메서드 compare_to(other) -> int. 무관한 클래스 둘 (Score(value)WordLength(word)) 다 compare_to 구현. 함수 sort_by_compare(items)compare_to 로 정렬한 항목 반환. 두 클래스 타입 다 같은 함수에서 작동 확인. 그 다음 ABC 로 다시 — 마찰 느껴 (Score 와 WordLength 가 다른 공통점 없는데 둘 다 상속해야).

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.