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
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' ✓
함수 process(message) 작성 — dict 받고 match 로 type 필드에 따라 분기. 세 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.