PEP 636 – 구조적 패턴 매칭: 튜토리얼
- Author:
- Daniel F Moisset <dfmoisset at gmail.com>
- Sponsor:
- Guido van Rossum <guido at python.org>
- BDFL-Delegate:
- Discussions-To:
- Python-Dev list
- Status:
- Final
- Type:
- Informational
- Created:
- 12-Sep-2020
- Python-Version:
- 3.10
- Post-History:
- 22-Oct-2020, 08-Feb-2021
- Resolution:
- Python-Committers message
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 PEP는 PEP 634에서 소개된 패턴 매칭에 대한 튜토리얼입니다.
PEP 622는 패턴 매칭을 위한 구문을 제안했으며, 커뮤니티와 Steering Council 모두로부터 상세한 논의를 받았습니다. 자주 제기된 우려는 이 기능을 얼마나 쉽게 설명하고 배울 수 있는가에 관한 것이었습니다. 이 PEP는 개발자들이 Python에서 패턴 매칭을 학습하는 데 사용할 수 있는 종류의 문서를 제공하여 이러한 우려에 대응합니다.
이는 PEP 634 (패턴 매칭의 기술 사양) 및 PEP 635 (패턴 매칭의 필요성에 대한 동기와 근거 및 설계 고려 사항)를 위한 지원 자료로 간주됩니다.
튜토리얼보다 빠른 검토를 원하는 독자는 Appendix A를 참조하십시오.
튜토리얼
이 튜토리얼의 동기를 부여하는 예제로 텍스트 어드벤처를 작성합니다. 이는 사용자가 텍스트 명령을 입력하여 가상의 세계와 상호 작용하고 발생한 일에 대한 텍스트 설명을 받는 인터랙티브 픽션의 한 형태입니다. 명령은 get sword, attack dragon, go north, enter shop 또는 buy cheese와 같은 자연어의 단순화된 형태입니다.
시퀀스 매칭
주 루프에서는 사용자로부터 입력을 받아 단어로 분할해야 하며, 예를 들어 다음과 같은 문자열 목록이 됩니다.:
command = input("What are you doing next? ")
# analyze the result of command.split()
다음 단계는 단어를 해석하는 것입니다. 대부분의 명령에는 동작과 객체라는 두 단어가 있습니다. 따라서 다음과 같이 작성하고 싶을 수 있습니다.:
[action, obj] = command.split()
... # interpret action, obj
해당 코드 줄의 문제는 무언가가 빠져 있다는 것입니다. 사용자가 2개보다 많거나 적은 단어를 입력하면 어떻게 됩니까? 이 문제를 방지하려면 단어 목록의 길이를 확인하거나, 위의 문장이 발생시킬 ValueError를 포착할 수 있습니다.
대신 매칭 문을 사용할 수 있습니다.:
match command.split():
case [action, obj]:
... # interpret action, obj
match 문은 “주제” (match 키워드 뒤의 값)를 평가하고, 이를 “패턴” (case 옆의 코드)과 대조합니다. 패턴은 두 가지 서로 다른 작업을 수행할 수 있습니다.
- 주제가 특정 구조를 가지는지 확인합니다. 이 경우
[action, obj]패턴은 정확히 두 요소로 이루어진 모든 시퀀스와 일치합니다. 이를 매칭이라고 합니다. - 패턴의 일부 이름을 주제의 구성 요소에 바인딩합니다. 이 경우 목록에 두 요소가 있으면
action = subject[0]및obj = subject[1]을 바인딩합니다.
일치하는 항목이 있으면 바인딩된 변수와 함께 case 블록 내부의 문이 실행됩니다. 일치하는 항목이 없으면 아무 일도 일어나지 않고 match뒤의 문이 다음에 실행됩니다.
언패킹 할당과 유사한 방식으로 괄호, 대괄호 또는 단순한 쉼표 구분을 서로 바꿔 사용할 수 있다는 점에 유의하십시오. 따라서 같은 의미로 case action, obj 또는 case (action, obj)를 작성할 수 있습니다. 모든 형식은 모든 시퀀스(예를 들어 목록이나 튜플)와 일치합니다.
여러 패턴 매칭
대부분의 명령이 동작/객체 형식이더라도, 길이가 서로 다른 사용자 명령을 사용하고 싶을 수 있습니다. 예를 들어 look이나 quit처럼 객체가 없는 단일 동사를 추가하고 싶을 수 있습니다. match 문에는 둘 이상의 case가 있을 수 있으며, 실제로 그럴 가능성이 높습니다.:
match command.split():
case [action]:
... # interpret single-verb action
case [action, obj]:
... # interpret action, obj
match 문은 위에서 아래 순서로 패턴을 확인합니다. 패턴이 대상과 일치하지 않으면 다음 패턴이 시도됩니다. 그러나 일치하는 첫 번째 패턴이 발견되면 해당 case의 본문이 실행되고, 이후의 모든 case는 무시됩니다. 이는 if/elif/elif/... 문이 작동하는 방식과 비슷합니다.
특정 값 매칭
코드에서는 여전히 특정 동작을 확인하고, 특정 동작에 따라 조건부로 다른 로직을 실행해야 합니다(예: quit, attack 또는 buy). if/elif/elif/...를 연쇄적으로 사용하거나 함수 딕셔너리를 사용하여 이를 수행할 수 있지만, 여기서는 패턴 매칭을 활용하여 이 작업을 해결합니다. 변수 대신 패턴에 리터럴 값(예: "quit", 42 또는 None)을 사용할 수 있습니다. 이를 통해 다음과 같이 작성할 수 있습니다.:
match command.split():
case ["quit"]:
print("Goodbye!")
quit_game()
case ["look"]:
current_room.describe()
case ["get", obj]:
character.get(obj, current_room)
case ["go", direction]:
current_room = current_room.neighbor(direction)
# The rest of your commands go here
["get", obj]와 같은 패턴은 첫 번째 요소가 "get"과 같은 2개 요소 시퀀스에만 일치합니다. 또한 obj = subject[1]을 바인딩합니다.
gocase에서 볼 수 있듯이, 서로 다른 패턴에서 서로 다른 변수 이름을 사용할 수도 있습니다.
리터럴 값은 == 연산자로 비교하지만, True, False 및 None 상수는 is 연산자로 비교합니다.
여러 값 매칭
플레이어는 drop key, drop sword, drop cheese 명령을 연속으로 사용하여 여러 아이템을 버릴 수 있습니다. 이 인터페이스는 번거로울 수 있으므로, drop key sword cheese처럼 하나의 명령으로 여러 아이템을 버릴 수 있도록 하고 싶을 수 있습니다. 이 경우 명령에 몇 개의 단어가 포함될지 미리 알 수 없지만, 할당에서 허용되는 것과 같은 방식으로 패턴에서 확장 언패킹을 사용할 수 있습니다.:
match command.split():
case ["drop", *objects]:
for obj in objects:
character.drop(obj, current_room)
# The rest of your commands go here
이는 첫 요소가 “drop”인 모든 시퀀스와 일치합니다. 나머지 모든 요소는 list 객체에 캡처되고 objects 변수에 바인딩됩니다.
이 구문에는 시퀀스 언패킹과 유사한 제한이 있습니다. 패턴에 별표 이름을 둘 이상 사용할 수 없습니다.
와일드카드 추가
모든 패턴이 실패했을 때 명령을 인식하지 못했다는 오류 메시지를 출력하고 싶을 수 있습니다. 방금 배운 기능을 사용하여 마지막 패턴으로 case [*ignored_words]를 작성할 수 있습니다. 하지만 훨씬 더 간단한 방법이 있습니다.:
match command.split():
case ["quit"]: ... # Code omitted for brevity
case ["go", direction]: ...
case ["drop", *objects]: ...
... # Other cases
case _:
print(f"Sorry, I couldn't understand {command!r}")
_로 작성하며 와일드카드라고 부르는 이 특수 패턴은 항상 일치하지만 어떤 변수도 바인딩하지 않습니다.
이는 시퀀스뿐만 아니라 모든 객체와 일치한다는 점에 유의하십시오. 따라서 이 패턴은 마지막 패턴으로 단독 사용해야만 의미가 있습니다(오류를 방지하기 위해 Python은 그보다 앞에서 사용하지 못하도록 합니다).
패턴 조합
지금까지 사용해 온 패턴이 어떻게 구성되는지 이해하기 위해 예제에서 한 걸음 물러나 살펴보기 좋은 시점입니다. 패턴은 서로 중첩될 수 있으며, 위의 예제에서 암묵적으로 그렇게 해 왔습니다.
지금까지 살펴본 “단순한” 패턴(“단순하다”는 여기서 다른 패턴을 포함하지 않는다는 의미입니다)이 몇 가지 있습니다.
- 캡처 패턴 (
direction,action,objects와 같은 독립적인 이름입니다.) 이러한 패턴을 따로 논의한 적은 없지만, 다른 패턴의 일부로 사용했습니다. - 리터럴 패턴 (문자열 리터럴, 숫자 리터럴,
True,False,None입니다.) - 와일드카드 패턴
_
지금까지 실험해 본 유일한 비단순 패턴은 시퀀스 패턴입니다. 실제로 시퀀스 패턴의 각 요소는 다른 어떤 패턴이든 될 수 있습니다. 따라서 ["first", (left, right), _, *rest]와 같은 패턴을 작성할 수 있습니다. 이 패턴은 최소 세 개의 요소로 이루어진 시퀀스인 대상과 일치하며, 첫 번째 요소는 "first"와 같고 두 번째 요소는 다시 두 요소로 이루어진 시퀀스입니다. 또한 left=subject[1][0], right=subject[1][1], rest = subject[3:]를 바인딩합니다.
Or 패턴
어드벤처 게임 예제로 돌아가면, 여러 패턴이 동일한 결과를 내도록 하고 싶을 수 있습니다. 예를 들어 north 명령과 go north 명령을 동등하게 만들고 싶을 수 있습니다. 또한 어떤 X에 대해서든 get X, pick up X, pick X up의 별칭을 원할 수도 있습니다.
패턴에서 | 기호는 패턴들을 대안으로 결합합니다. 예를 들어 다음과 같이 작성할 수 있습니다.:
match command.split():
... # Other cases
case ["north"] | ["go", "north"]:
current_room = current_room.neighbor("north")
case ["get", obj] | ["pick", "up", obj] | ["pick", obj, "up"]:
... # Code for picking up the given object
이를 or 패턴이라고 하며 예상한 결과를 생성합니다. 패턴은 왼쪽에서 오른쪽 순서로 시도되므로, 둘 이상의 대안이 일치할 때 무엇이 바인딩되는지 알아두는 것이 중요할 수 있습니다. or 패턴을 작성할 때의 중요한 제약은 모든 대안이 동일한 변수를 바인딩해야 한다는 것입니다. 따라서 [1, x] | [2, y] 패턴은 성공적으로 일치한 후 어느 변수가 바인딩될지 명확하지 않게 만들기 때문에 허용되지 않습니다. [1, x] | [2, x]는 완전히 올바르며 성공하면 항상 x를 바인딩합니다.
일치한 하위 패턴 캡처
“go” 명령의 첫 번째 버전은 ["go", direction] 패턴으로 작성했습니다. 마지막 버전에서 ["north"] | ["go", "north"] 패턴을 사용해 변경한 방식은 이에 비해 장점도 있지만 단점도 있습니다. 최신 버전은 별칭을 허용하지만 방향도 하드코딩하므로, 실제로 북쪽/남쪽/동쪽/서쪽에 대해 별도의 패턴을 작성해야 합니다. 이로 인해 코드가 일부 중복되지만, 동시에 입력 검증이 더 좋아지며, 사용자가 입력한 명령이 방향이 아니라 "go figure!"인 경우에는 해당 분기로 들어가지 않게 됩니다.
다음과 같이 두 방식의 장점을 모두 얻어 보겠습니다(“go”가 없는 별칭 버전은 간결성을 위해 생략하겠습니다).:
match command.split():
case ["go", ("north" | "south" | "east" | "west")]:
current_room = current_room.neighbor(...)
# how do I know which direction to go?
이 코드는 하나의 분기이며, “go” 다음 단어가 실제로 방향인지 확인합니다. 그러나 플레이어를 이동시키는 코드는 어느 방향이 선택되었는지 알아야 하는데, 그렇게 할 방법이 없습니다. 우리에게 필요한 것은 or 패턴처럼 동작하면서 동시에 캡처도 수행하는 패턴입니다. 이를 as 패턴으로 수행할 수 있습니다.:
match command.split():
case ["go", ("north" | "south" | "east" | "west") as direction]:
current_room = current_room.neighbor(direction)
as 패턴은 왼쪽에 있는 어떤 패턴과도 일치하면서, 동시에 그 값을 이름에 바인딩합니다.
패턴에 조건 추가
위에서 살펴본 패턴은 강력한 데이터 필터링을 수행할 수 있지만, 때로는 불리언 표현식의 모든 기능을 사용하고 싶을 수도 있습니다. 현재 방에서 가능한 출구를 기준으로 제한된 방향 집합에서만 “go” 명령을 허용하고 싶다고 가정하겠습니다. case에 guard를 추가하면 이를 구현할 수 있습니다. 가드는 if키워드 뒤에 임의의 표현식이 오는 형태로 구성됩니다.:
match command.split():
case ["go", direction] if direction in current_room.exits:
current_room = current_room.neighbor(direction)
case ["go", _]:
print("Sorry, you can't go that way")
가드는 패턴의 일부가 아니라 case의 일부입니다. 가드는 패턴이 일치하고 모든 패턴 변수가 바인딩된 후에만 검사됩니다(따라서 위의 예제에서 조건이 direction변수를 사용할 수 있습니다). 패턴이 일치하고 조건이 참이면 case의 본문이 정상적으로 실행됩니다. 패턴이 일치하지만 조건이 거짓이면, match 문은 패턴이 일치하지 않았던 것처럼 다음 case를 검사합니다(일부 변수가 이미 바인딩되었을 수 있다는 부작용이 있습니다).
UI 추가: 객체 매칭
모험 게임이 성공을 거두고 있으며 그래픽 인터페이스를 구현해 달라는 요청을 받았습니다. 선택한 UI 툴킷을 사용하면 이벤트 루프를 작성하고 event.get()을 호출하여 새 이벤트 객체를 가져올 수 있습니다. 결과 객체는 사용자 동작에 따라 서로 다른 유형과 속성을 가질 수 있습니다. 예를 들면 다음과 같습니다.
- 사용자가 키를 누르면
KeyPress객체가 생성됩니다. 이 객체에는 누른 키의 이름을 담은key_name속성과 수정 키에 관한 몇 가지 다른 속성이 있습니다. - 사용자가 마우스를 클릭하면
Click객체가 생성됩니다. 이 객체에는 포인터의 좌표를 담은position속성이 있습니다. - 사용자가 게임 창의 닫기 버튼을 클릭하면
Quit객체가 생성됩니다.
여러 isinstance()검사를 작성하는 대신, 패턴을 사용하여 다양한 종류의 객체를 식별하고 해당 객체의 속성에도 패턴을 적용할 수 있습니다.:
match event.get():
case Click(position=(x, y)):
handle_click_at(x, y)
case KeyPress(key_name="Q") | Quit():
game.quit()
case KeyPress(key_name="up arrow"):
game.go_north()
...
case KeyPress():
pass # Ignore other keystrokes
case other_event:
raise ValueError(f"Unrecognized event: {other_event}")
Click(position=(x, y))와 같은 패턴은 이벤트의 유형이 Click 클래스의 서브클래스인 경우에만 일치합니다. 또한 이벤트에 position 속성이 있고 이 속성이 (x, y) 패턴과 일치해야 합니다. 일치하면 로컬 변수 x와 y에 예상 값이 할당됩니다.
인수가 없는 KeyPress()와 같은 패턴은 KeyPress 클래스의 인스턴스인 모든 객체와 일치합니다. 패턴에서 지정한 속성만 매칭되고 다른 속성은 모두 무시됩니다.
위치 속성 매칭
이전 섹션에서는 객체 매칭을 수행할 때 이름이 지정된 속성을 매칭하는 방법을 설명했습니다. 일부 객체에서는 매칭된 인자를 위치로 설명하는 것이 편리할 수 있습니다(특히 속성이 몇 개뿐이고 “표준” 순서가 있는 경우). 사용하는 클래스가 명명된 튜플이나 데이터 클래스라면 객체를 생성할 때 사용하는 순서와 동일한 순서를 따라 이를 수행할 수 있습니다. 예를 들어, 위의 UI 프레임워크가 클래스를 다음과 같이 정의한다면:
from dataclasses import dataclass
@dataclass
class Click:
position: tuple
button: Button
그러면 위의 match 문을 다음과 같이 다시 작성할 수 있습니다.:
match event.get():
case Click((x, y)):
handle_click_at(x, y)
패턴의 첫 번째 인자가 데이터 클래스 정의의 첫 번째 속성에 대응하기 때문에 (x, y) 패턴은 position 속성에 대해 자동으로 매칭됩니다.
다른 클래스에는 속성의 자연스러운 순서가 없으므로, 패턴에서 속성과 매칭하려면 이름을 명시적으로 사용해야 합니다. 그러나 속성의 순서를 수동으로 지정하여 위치 매칭을 허용할 수 있으며, 이 대체 정의에서는 다음과 같이 작성합니다.:
class Click:
__match_args__ = ("position", "button")
def __init__(self, pos, btn):
self.position = pos
self.button = btn
...
__match_args__ 특수 속성은 case Click((x,y))와 같은 패턴에서 사용할 수 있는 속성의 명시적 순서를 정의합니다.
상수 및 열거형과의 매칭
위의 패턴은 모든 마우스 버튼을 동일하게 처리하지만, 왼쪽 클릭은 허용하고 다른 버튼은 무시하려고 합니다. 이 과정에서 button 속성의 타입이 Button으로 지정되어 있으며, 이는 enum.Enum으로 만든 열거형이라는 사실을 알게 됩니다. 실제로 다음과 같이 열거형 값과 매칭할 수 있습니다.:
match event.get():
case Click((x, y), button=Button.LEFT): # This is a left click
handle_click_at(x, y)
case Click():
pass # ignore other clicks
이는 math.pi와 같은 점 표기 이름이라면 무엇이든 작동합니다. 그러나 수식어가 없는 이름, 즉 점이 없는 단독 이름은 항상 캡처 패턴으로 해석되므로, 패턴에서는 항상 정규화된 상수를 사용하여 이러한 모호성을 피해야 합니다.
클라우드로: 매핑
게임의 온라인 버전을 만들기로 결정했습니다. 모든 로직은 서버에 두고, UI는 JSON 메시지를 사용해 통신하는 클라이언트에 둘 것입니다. json모듈을 통해 이러한 메시지는 파이썬 딕셔너리, 리스트 및 기타 내장 객체로 매핑됩니다.
우리 클라이언트는 취할 동작들의 딕셔너리(JSON에서 파싱된) 리스트를 받게 되며, 각 요소는 예를 들어 다음과 같은 모습입니다:
{"text": "The shop keeper says 'Ah! We have Camembert, yes sir'", "color": "blue"}- 클라이언트가 일시 정지를 해야 하는 경우
{"sleep": 3} - 사운드를 재생하려면
{"sound": "filename.ogg", "format": "ogg"}을 사용합니다.
지금까지 패턴은 시퀀스를 처리했지만, 현재 존재하는 키를 기준으로 매핑에 매칭하는 패턴도 있습니다. 이 경우 다음과 같이 작성할 수 있습니다.:
for action in actions:
match action:
case {"text": message, "color": c}:
ui.set_text_color(c)
ui.display(message)
case {"sleep": duration}:
ui.wait(duration)
case {"sound": url, "format": "ogg"}:
ui.play(url)
case {"sound": _, "format": _}:
warning("Unsupported audio format")
매핑 패턴의 키는 리터럴이어야 하지만, 값은 어떤 패턴이든 될 수 있습니다. 시퀀스 패턴과 마찬가지로, 전체 패턴이 매칭되려면 모든 하위 패턴이 매칭되어야 합니다.
매핑 패턴 안에서 **rest를 사용하여 대상의 추가 키를 캡처할 수 있습니다. 이를 생략하면 매칭할 때 대상의 추가 키는 무시된다는 점에 유의하십시오. 즉, {"text": "foo", "color": "red", "style": "bold"} 메시지는 위 예의 첫 번째 패턴과 매칭됩니다.
내장 클래스와의 매칭
위의 코드에는 유효성 검사를 추가할 수 있습니다. 메시지가 외부 소스에서 전달되므로 필드의 타입이 잘못되어 버그나 보안 문제가 발생할 수 있습니다.
모든 클래스는 유효한 매칭 대상이며, 여기에는 bool, str 또는 int와 같은 내장 클래스도 포함됩니다. 이를 통해 위의 코드와 클래스 패턴을 결합할 수 있습니다. 따라서 {"text": message, "color": c} 대신 {"text": str() as message, "color": str() as c}를 사용하여 message와 c가 모두 문자열인지 확인할 수 있습니다. 여러 내장 클래스에서는 (PEP 634를 참조하면 전체 목록을 확인할 수 있습니다) 위치 매개변수를 약칭으로 사용할 수 있으므로, str() as c 대신 str(c)로 작성할 수 있습니다. 완전히 다시 작성한 버전은 다음과 같습니다.:
for action in actions:
match action:
case {"text": str(message), "color": str(c)}:
ui.set_text_color(c)
ui.display(message)
case {"sleep": float(duration)}:
ui.wait(duration)
case {"sound": str(url), "format": "ogg"}:
ui.play(url)
case {"sound": _, "format": _}:
warning("Unsupported audio format")
부록 A – 간단한 소개
match 문은 표현식을 받아 그 값을 하나 이상의 case 블록으로 주어진 연속된 패턴과 비교합니다. 이는 표면적으로 C, Java 또는 JavaScript(그리고 다른 많은 언어)의 switch 문과 비슷해 보이지만, 훨씬 더 강력합니다.
가장 단순한 형태는 대상 값을 하나 이상의 리터럴과 비교합니다:
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong with the Internet"
마지막 블록을 주목하십시오: “변수 이름” _는 와일드카드로 동작하며 매칭에 결코 실패하지 않습니다.
|(“or”)를 사용하여 여러 리터럴을 하나의 패턴으로 결합할 수 있습니다:
case 401 | 403 | 404:
return "Not allowed"
패턴은 언패킹 대입문처럼 보일 수 있으며, 변수를 바인딩하는 데 사용할 수 있습니다:
# point is an (x, y) tuple
match point:
case (0, 0):
print("Origin")
case (0, y):
print(f"Y={y}")
case (x, 0):
print(f"X={x}")
case (x, y):
print(f"X={x}, Y={y}")
case _:
raise ValueError("Not a point")
이것을 주의 깊게 살펴보십시오! 첫 번째 패턴은 두 개의 리터럴을 가지며, 위에서 보인 리터럴 패턴의 확장으로 생각할 수 있습니다. 하지만 다음 두 패턴은 리터럴과 변수를 결합하며, 변수는 대상(point)에서 값을 바인딩합니다. 네 번째 패턴은 두 개의 값을 캡처하는데, 이는 개념적으로 언패킹 대입문 (x, y) = point와 유사합니다.
데이터를 구조화하는 데 클래스를 사용하고 있다면, 클래스 이름 뒤에 생성자와 비슷한 인자 목록을 사용할 수 있으며, 속성을 변수로 캡처하는 기능도 사용할 수 있습니다:
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
def where_is(point):
match point:
case Point(x=0, y=0):
print("Origin")
case Point(x=0, y=y):
print(f"Y={y}")
case Point(x=x, y=0):
print(f"X={x}")
case Point():
print("Somewhere else")
case _:
print("Not a point")
속성에 순서를 제공하는 일부 내장 클래스(예: 데이터클래스)와 함께 위치 매개변수를 사용할 수 있습니다. 클래스에 __match_args__ 특수 속성을 설정하여 패턴에서 속성의 특정 위치를 정의할 수도 있습니다. 이 속성이 (“x”, “y”)로 설정되어 있다면, 다음 패턴들은 모두 동등합니다(그리고 모두 y 속성을 var 변수에 바인딩합니다):
Point(1, var)
Point(1, y=var)
Point(x=1, y=var)
Point(y=var, x=1)
패턴은 임의로 중첩될 수 있습니다. 예를 들어, 짧은 점들의 목록이 있다면 다음과 같이 매칭할 수 있습니다:
match points:
case []:
print("No points")
case [Point(0, 0)]:
print("The origin")
case [Point(x, y)]:
print(f"Single point {x}, {y}")
case [Point(0, y1), Point(0, y2)]:
print(f"Two on the Y axis at {y1}, {y2}")
case _:
print("Something else")
패턴에 “가드”라고 알려진 if 절을 추가할 수 있습니다. 가드가 거짓이면 match는 다음 case 블록을 계속 시도합니다. 값 캡처는 가드가 평가되기 전에 일어난다는 점을 유의하십시오:
match point:
case Point(x, y) if x == y:
print(f"Y=X at {x}")
case Point(x, y):
print(f"Not on the diagonal")
그 밖의 몇 가지 주요 기능:
- 언패킹 대입문과 마찬가지로, 튜플과 리스트 패턴은 정확히 같은 의미를 가지며 실제로 임의의 시퀀스와 매칭됩니다. 중요한 예외는 이터레이터나 문자열과는 매칭되지 않는다는 것입니다. (기술적으로, 대상은 반드시
collections.abc.Sequence의 인스턴스여야 합니다.) - 시퀀스 패턴은 와일드카드를 지원합니다:
[x, y, *rest]와(x, y, *rest)는 언패킹 대입문의 와일드카드와 유사하게 동작합니다.*뒤의 이름은_일 수도 있으므로,(x, y, *_)는 나머지 항목을 바인딩하지 않고 최소 두 개 이상의 항목으로 구성된 시퀀스와 일치합니다. - 매핑 패턴:
{"bandwidth": b, "latency": l}는 딕셔너리에서"bandwidth"와"latency"값을 캡처합니다. 시퀀스 패턴과 달리, 추가 키는 무시됩니다. 와일드카드**rest도 지원됩니다. (하지만**_는 중복이므로 허용되지 않습니다.) - 하위 패턴은
as키워드를 사용하여 캡처할 수 있습니다.:case (Point(x1, y1), Point(x2, y2) as p2): ...
- 대부분의 리터럴은 동등성으로 비교되지만, 싱글턴
True,False,None은 동일성으로 비교됩니다. - 패턴은 명명된 상수를 사용할 수 있습니다. 이들은 캡처 변수로 해석되는 것을 방지하기 위해 점으로 구분된 이름이어야 합니다.:
from enum import Enum class Color(Enum): RED = 0 GREEN = 1 BLUE = 2 match color: case Color.RED: print("I see red!") case Color.GREEN: print("Grass is green") case Color.BLUE: print("I'm feeling the blues :(")
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.