본문으로 건너뛰기
개발 머꼬
개발 노트Python
hohyeon.dev15

match 문에서 상수와 비교하려 했는데 모든 값이 첫 분기에 걸린 이유

  • #Engineering Note
  • #Python
  • #Syntax

문제 발생

상태 문자열을 상수로 정의해 두고 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), 키워드 인자 이름, 클래스 이름은 절대 대입되지 않습니다.

해결 방안

  1. 리터럴로 비교하거나, 점이 있는 이름을 씁니다. 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처럼 쓰는 것도 같은 이유로 동작합니다.

  1. 가드를 쓰면 임의의 조건을 붙일 수 있습니다. 값 캡처는 가드 평가보다 먼저 일어납니다.
match point:
    case Point(x, y) if x == y:
        return "대각선 위"
    case Point(x, y):
        return "대각선 밖"
  1. 클래스 패턴으로 분해합니다. 키워드 형태는 속성 이름을 그대로 쓰고, 위치 형태를 쓰려면 __match_args__가 필요합니다(dataclass는 자동으로 생성합니다).
match point:
    case Point(x=0, y=0):
        return "원점"
    case Point(x=x, y=0):
        return f"x축 위 {x}"
  1. 와일드카드 _를 마지막에 둡니다. _는 캡처하지 않는 특수 이름입니다. 이것 없이 아무 분기도 매칭되지 않으면 match 문은 조용히 아무 일도 하지 않습니다 — 예외가 나지 않으므로 놓치기 쉽습니다.
  2. match는 Python 3.10부터입니다. 그 이하를 지원해야 한다면 쓸 수 없습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.