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

match 문 — 구조 패턴 매칭 (3.10+)

~20 min · match, pattern-matching, switch, 3.10

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

switch 문이 아니야 — 더 이상하고 더 유용해

Python 3.10 이 match 를 추가했어. 표면상 C 계열 언어의 switch 처럼 보이는데 — 아니야. 구조 패턴 매칭 이야. 단순 동등 체크가 아니라 *값의 모양* 을 묘사하는 거야 (3 개짜리 list, 이런 키 가진 dict, 이런 속성 가진 클래스 인스턴스). 그리고 Python 이 매치된 부분을 이름 붙은 변수로 알아서 풀어줘.

Case — literal / capture / sequence / mapping / class

흔한 패턴 다섯. Literal — case 0:, case "x":. Capture — case x: 가 뭐든 x 에 바인딩. Sequence — case [a, b, *rest]: 가 list 풀어서. Mapping — case {"name": name}: 가 키 잡아. Class — case Point(x=0, y=y): 가 x=0 인 Point 매치 + y 바인딩. _ 와일드카드가 catch-all.

OR 패턴 / guard / AS 절

패턴은 | 로 합칠 수 있어 — case 1 | 2 | 3:. Guard (if 가 패턴 뒤) 는 매치 조건을 좁혀 — case x if x > 0:. as 절은 서브패턴에 이름 붙여 — case [Point(x, y) as p, *_]: 가 첫 원소 전체를 p 에 바인딩.

match 를 *안 써야* 할 때

분기가 단순 동등 체크 — case 1: case 2: case 3: — 면 dict dispatch 나 if 체인이 똑같이 깔끔. match 는 *구조화된 값을 풀어야 할 때* 진가 — 파싱된 AST, JSON 모양 데이터, 커스텀 클래스. C 스타일 switch 용도라면 match 안 필요했어. *복잡한 값을 분해할 때* 만큼은 Python 의 가장 깔끔한 도구.

주의: case 패턴의 *bare name 은 capture* 야 — 비교가 아냐. case foo: 는 뭐든 매치하고 foo 에 바인딩. 상수 변수와 비교하려면 dotted name (case Status.ACTIVE:) 또는 감싼 형태 (case (CONST,):). 가장 흔한 혼란.

Code

기본 match — literal / 와일드카드·python
def describe(status):
    match status:
        case 200:
            return "OK"
        case 301 | 302:
            return "redirect"
        case 404:
            return "not found"
        case n if 500 <= n < 600:
            return "server error"
        case _:
            return "unknown"

print(describe(200))      # OK
print(describe(301))      # redirect
print(describe(503))      # server error
print(describe(700))      # unknown
Sequence 패턴 — list/tuple 풀기·python
def parse(command):
    match command.split():
        case ["quit"]:
            return "exit"
        case ["go", direction]:
            return f"moving {direction}"
        case ["go", direction, distance]:
            return f"moving {direction} by {distance}"
        case [action, *args]:
            return f"action={action}, args={args}"
        case []:
            return "empty command"

print(parse("quit"))               # exit
print(parse("go north"))           # moving north
print(parse("go north 5"))         # moving north by 5
print(parse("jump high quickly"))  # action=jump, args=['high', 'quickly']
print(parse(""))                   # empty command
Mapping 패턴 — JSON 모양 데이터·python
def handle(event):
    match event:
        case {"type": "login", "user": user}:
            return f"login: {user}"
        case {"type": "logout", "user": user, "reason": reason}:
            return f"logout: {user} ({reason})"
        case {"type": "error", **rest}:
            return f"error event: {rest}"
        case _:
            return "unhandled"

print(handle({"type": "login", "user": "alice"}))
print(handle({"type": "logout", "user": "alice", "reason": "timeout"}))
print(handle({"type": "error", "code": 500, "msg": "oops"}))
Class 패턴 — 인스턴스 풀기·python
from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

@dataclass
class Circle:
    center: Point
    radius: float

def shape_summary(shape):
    match shape:
        case Point(0, 0):
            return "원점"
        case Point(x, 0):
            return f"x 축 위 {x}"
        case Point(x, y):
            return f"점 ({x}, {y})"
        case Circle(Point(0, 0), r):
            return f"원점 중심 원, r={r}"
        case Circle(_, r) if r > 100:
            return f"큰 원 r={r}"
        case _:
            return "unknown"

print(shape_summary(Point(0, 0)))
print(shape_summary(Point(3, 0)))
print(shape_summary(Circle(Point(0, 0), 5)))
capture vs 상수 함정·python
ACTIVE = "active"
INACTIVE = "inactive"

def status_of(s):
    match s:
        case ACTIVE:           # 잘못됨 — 이건 CAPTURE, 뭐든 매치
            return "is active"
        case _:
            return "unknown"

print(status_of("foo"))   # 'is active'  <- 버그! 'foo' 가 ACTIVE 로 캡쳐됨

# 맞음 — dotted name (enum 또는 클래스 속성)
class Status:
    ACTIVE = "active"
    INACTIVE = "inactive"

def status_of_v2(s):
    match s:
        case Status.ACTIVE:    # 맞음 — 상수와 비교
            return "is active"
        case _:
            return "unknown"

print(status_of_v2("foo"))    # 'unknown'  ✓
print(status_of_v2("active")) # 'is active'  ✓

External links

Exercise

함수 process(message) 작성 — dict 받고 matchtype 필드에 따라 분기. 세 case — {"type": "chat", "text": ...} 는 text 대문자로, {"type": "command", "name": ..., "args": [...]} 는 명령어 이름 + 인자 개수 문자열, {"type": "event", "name": ..., **rest} 는 이벤트 이름 + 추가 필드 개수. 알 수 없는 모양은 와일드카드로 "unknown". 최소 4 메시지로 테스트.

Progress

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

댓글 0

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

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