match 문에서 상수와 비교하려 했는데 모든 값이 첫 분기에 걸린 이유
문제 발생
상태 문자열을 상수로 정의해 두고 match로 분기했는데, 어떤 값을 넣어도 항상 첫 번째 분기가 실행됐습니다.
ACTIVE = "active"
INACTIVE = "inactive"
def describe(status):
match status:
case ACTIVE: # 항상 여기로 온다
return "활성"
case INACTIVE:
return "비활성"
case _:
return "알 수 없음"
describe("inactive") # "활성"원인 분석
case ACTIVE:의 ACTIVE는 비교 대상이 아니라 캡처 패턴입니다. 단순 이름(점이 없는 이름)은 무엇이든 매칭시키고 그 이름에 값을 대입합니다. 즉 위 코드는 "status가 ACTIVE와 같으면"이 아니라 "status를 ACTIVE라는 이름에 담아라"입니다. 무엇이든 매칭되므로 첫 분기에서 끝납니다. 게다가 모듈 전역의 ACTIVE가 그 안에서 가려지기까지 합니다.
파이썬 문서는 이 규칙을 이렇게 설명합니다 — 패턴은 대입문의 왼쪽에 오는 것처럼 읽으면 되고, 단독으로 쓰인 이름만 대입 대상이 됩니다. 반대로 점이 있는 이름(foo.bar), 키워드 인자 이름, 클래스 이름은 절대 대입되지 않습니다.
해결 방안
- 리터럴로 비교하거나, 점이 있는 이름을 씁니다.
Enum이 가장 읽기 좋습니다.
from enum import Enum
class Status(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
def describe(status):
match status:
case Status.ACTIVE: # 점이 있으므로 값 비교
return "활성"
case Status.INACTIVE:
return "비활성"
case _:
return "알 수 없음"상수를 모아 둔 모듈을 import해 constants.ACTIVE처럼 쓰는 것도 같은 이유로 동작합니다.
- 가드를 쓰면 임의의 조건을 붙일 수 있습니다. 값 캡처는 가드 평가보다 먼저 일어납니다.
match point:
case Point(x, y) if x == y:
return "대각선 위"
case Point(x, y):
return "대각선 밖"- 클래스 패턴으로 분해합니다. 키워드 형태는 속성 이름을 그대로 쓰고, 위치 형태를 쓰려면
__match_args__가 필요합니다(dataclass는 자동으로 생성합니다).
match point:
case Point(x=0, y=0):
return "원점"
case Point(x=x, y=0):
return f"x축 위 {x}"- 와일드카드
_를 마지막에 둡니다._는 캡처하지 않는 특수 이름입니다. 이것 없이 아무 분기도 매칭되지 않으면match문은 조용히 아무 일도 하지 않습니다 — 예외가 나지 않으므로 놓치기 쉽습니다. match는 Python 3.10부터입니다. 그 이하를 지원해야 한다면 쓸 수 없습니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.