본문 바로가기
C.W.K.
Stream
Lesson 07 of 07 · published

Protocol과 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

duck typing은 행동을 먼저 본다

호출에 필요한 메서드가 있으면 구체 타입과 상속 계보를 묻지 않고 쓸 수 있어. 유연하지만 문서와 정적 도구가 없으면 계약이 늦게 드러날 수 있어.

Protocol은 구조를 선언해

어떤 메서드와 속성이 필요한지 적으면 명시적으로 상속하지 않은 기존 클래스도 그 모양을 만족할 수 있어. 호출자가 인터페이스를 소유하고 외부 타입까지 받아야 할 때 잘 맞아.

ABC는 명시적인 가족을 만든다

추상 메서드와 공통 구현을 제공하고 하위 클래스가 계보에 들어오게 해. 프레임워크가 생성·수명·공통 동작을 통제해야 한다면 ABC가 더 분명해.

Code

행동만 믿는 duck typing·python
def total_chars(thing):
    # 타입을 미리 검사하지 않고 그냥 호출. 글자 줄 방법 있으면 작동.
    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을 상속하지 않음.
# 구조로 맞는 것이지 명목 관계가 아님.
추상 메서드와 명시적 상속을 가진 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'

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

# area를 구현하지 않은 서브클래스도 인스턴스화할 수 없음
class Broken(Shape):
    pass

try:
    Broken()
except TypeError as e:
    print(e)
Protocol과 ABC 고르는 기준·python
# Protocol 사용 시기 —
# - 유연성 원함 (직접 소유하지 않은 third-party 클래스도 매치 가능)
# - 공유 구현 없이 모양만 계약
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의 다섯 brain은 Adapter의 streaming 계약을 공유해.
# provider별 route와 복구 동작은 각 variant가 따로 책임져.

External links

Exercise

compare_to(other) -> int를 요구하는 Comparable Protocol을 만들고 서로 무관한 Score와 WordLength가 만족하게 해. 두 타입을 같은 sort_by_compare 함수에서 정렬한 뒤 ABC로 다시 만들어 불필요한 공통 상속이 어떤 마찰을 주는지 비교해.

Progress

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

댓글 0

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

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