[Final] PEP 635 - Structural Pattern Matching: Motivation and Rationale
원문 링크: PEP 635 - Structural Pattern Matching: Motivation and Rationale
상태: Final 유형: Informational 작성일: 12-Sep-2020
PEP 635 – 구조적 패턴 매칭: 동기 및 근거 (Structural Pattern Matching: Motivation and Rationale) 번역 및 정리
개요
이 문서는 Python 3.10에 도입된 구조적 패턴 매칭(Structural Pattern Matching)의 동기와 설계 배경을 설명하는 PEP 635의 번역 및 요약본입니다. PEP 634(“Structural Pattern Matching: Specification”)의 제안 내용을 뒷받침하며, 패턴의 개념, 구문, 의미론에 대한 보다 쉬운 소개는 PEP 636을 참고하도록 권장합니다.
목표
Python 개발자들이 이 PEP의 제안 내용, 도입 배경, 그리고 실제 Python 사용에 미치는 영향을 명확하게 이해할 수 있도록 돕는 것이 이 문서의 목표입니다.
초록 (Abstract)
이 PEP는 PEP 634(“Structural Pattern Matching: Specification”)에 대한 동기(motivation)와 근거(rationale)를 제공합니다. 처음 읽는 독자들은 패턴의 개념, 구문, 의미론에 대한 더 쉬운 소개를 제공하는 PEP 636부터 시작하는 것이 좋습니다.
동기 (Motivation)
구조적 패턴 매칭 구문은 Haskell, Erlang, Scala부터 Elixir, Ruby에 이르기까지 많은 언어에서 찾아볼 수 있습니다. Python은 이미 시퀀스 언패킹(sequence unpacking) 할당을 통해 제한적인 형태를 지원하고 있으며, 이 새로운 제안은 이를 활용합니다.
이 기능은 다음과 같은 일반적인 Python 관용구를 더 우아하게 표현할 수 있도록 돕습니다.
1. if ... elif ... else
관용구 대체
객체의 타입이나 형태를 isinstance()
, hasattr()
, len()
등의 체크를 통해 확인하고, 그에 따라 다른 코드 블록을 실행하는 if ... elif ... else
구조는 종종 복잡해질 수 있습니다.
기존 코드 예시:
if isinstance(x, tuple) and len(x) == 2:
host, port = x
mode = "http"
elif isinstance(x, tuple) and len(x) == 3:
host, port, mode = x
# 기타 등등
match
를 사용한 더 우아한 표현:
match x:
case host, port:
mode = "http"
case host, port, mode:
pass
# 기타 등등
match
문을 사용하면 코드를 더 간결하고 읽기 쉽게 만들 수 있습니다.
2. AST(Abstract Syntax Tree) 순회 코드 간소화
AST 순회 코드는 특정 패턴과 일치하는 노드를 찾을 때 자주 사용됩니다.
기존 코드 예시:
if (isinstance(node, BinOp) and node.op == "+" and isinstance(node.right, BinOp) and node.right.op == "*"):
a, b, c = node.left, node.right.left, node.right.right
# a + b*c 처리
match
를 사용한 더 읽기 쉬운 표현:
match node:
case BinOp("+", a, BinOp("*", b, c)):
# a + b*c 처리
패턴 매칭을 Python에 추가함으로써 이러한 예시들과 같이 더 깔끔하고 읽기 쉬운 코드를 작성할 수 있게 될 것이라고 기대됩니다.
패턴 매칭과 객체 지향 (Pattern Matching and OO)
패턴 매칭은 객체 지향 패러다임에 상호 보완적입니다. 객체 지향과 상속을 통해 기본 클래스에서 특정 연산에 대한 기본 동작을 정의하고 서브클래스에서 이를 오버라이드할 수 있습니다. 또한 Visitor 패턴을 사용하여 동작과 데이터를 분리할 수도 있습니다.
하지만 이것만으로는 모든 상황에 충분하지 않습니다. 예를 들어, 코드 생성기가 AST를 소비할 때, 생성될 코드가 노드의 클래스뿐만 아니라 일부 클래스 속성(예: 위 BinOp
예시)의 값에 따라 달라져야 하는 경우가 많습니다. Visitor 패턴은 노드의 클래스만을 기반으로 선택할 수 있기 때문에 이러한 경우에 충분히 유연하지 않습니다.
패턴 매칭은 Visitor 패턴과 마찬가지로 관심사의 엄격한 분리를 허용합니다. 특정 동작이나 데이터 처리는 클래스 계층이나 조작되는 객체와 독립적입니다. 특히 미리 정의되거나 내장된 클래스를 다룰 때는 개별 클래스에 추가 메서드를 추가하는 것이 불가능한 경우가 많습니다. 패턴 매칭은 Visitor 패턴에 필요한 상용구 코드(boilerplate code)의 부담을 덜어줄 뿐만 아니라, 내장 타입과 직접 작동할 수 있을 만큼 유연합니다. 또한 패턴 매칭은 상속을 자동으로 고려합니다. 즉, C를 상속하는 클래스 D는 기본적으로 C를 대상으로 하는 패턴에 의해 처리됩니다.
객체 지향 프로그래밍은 단일 디스패치(single-dispatch)에 맞춰져 있습니다. 즉, 단일 인스턴스(또는 해당 타입)가 어떤 메서드가 호출될지 결정합니다. 패턴 매칭은 여러 객체의 타입에 따라 취할 동작이 동일하게 결정되는 다중 디스패치(multi-dispatch)와 같은 상황을 처리하는 데 구조적으로 더 적합합니다.
패턴과 함수형 스타일 (Patterns and Functional Style)
많은 Python 애플리케이션과 라이브러리는 일관된 OO 스타일로 작성되지 않습니다. Java와 달리 Python은 모듈의 최상위 수준에서 함수를 정의하는 것을 권장하며, 간단한 데이터 구조의 경우 튜플(또는 named tuple, 리스트)과 딕셔너리가 클래스나 데이터 클래스와 혼합되어 사용되거나 단독으로 사용되는 경우가 많습니다.
패턴 매칭은 이러한 데이터 구조를 분해(picking apart)하는 데 특히 적합합니다. 예를 들어, match
를 사용하여 JSON 데이터 구조를 선택하는 코드를 쉽게 작성할 수 있습니다.
match json_pet:
case {"type": "cat", "name": name, "pattern": pattern}:
return Cat(name, pattern)
case {"type": "dog", "name": name, "breed": breed}:
return Dog(name, breed)
case _:
raise ValueError("Not a suitable pet")
함수형 프로그래밍은 일반적으로 데이터 내의 관계에 초점을 맞춘 선언적 스타일을 선호하며, 가능한 한 부작용(side effects)을 피합니다. 따라서 패턴 매칭은 함수형 프로그래밍 스타일에 자연스럽게 부합하며 강력하게 지원합니다.
근거 (Rationale)
이 섹션은 개별 설계 결정에 대한 근거를 제공하며, 표준 PEP 형식의 “Rejected ideas” 부분을 대체합니다. 이는 사양(PEP 634)에 해당하는 섹션으로 구성됩니다.
개요 및 용어 (Overview and Terminology)
패턴 매칭의 많은 힘은 서브패턴(subpatterns)의 중첩(nesting)에서 나옵니다. 패턴 매치의 성공이 서브패턴의 성공에 직접적으로 의존한다는 것은 설계의 핵심 요소입니다. 그러나 P(Q(), R())
와 같은 패턴은 Q()
와 R()
두 서브패턴이 모두 성공해야만 성공하지만(P
의 성공은 Q
와 R
에 의존함), 패턴 P
가 먼저 검사됩니다. P
가 실패하면 Q()
나 R()
는 시도되지 않습니다.
또한, 패턴은 값을 할당하는 대신 이름에 값을 바인딩(bind)합니다. 이는 패턴이 부작용을 갖지 않는다는 사실을 반영하며, Capture 또는 AS 패턴이 속성이나 서브스크립트에 값을 할당할 수 없음을 의미합니다. 따라서 전통적인 할당과 패턴에서의 이름 바인딩 간의 미묘한 차이를 강조하기 위해 ‘할당(assign)’ 대신 일관되게 ‘바인딩(bind)’이라는 용어를 사용합니다.
match
문 (The Match Statement)
match
문은 표현식을 평가하여 대상(subject)을 생성하고, 대상과 일치하는 첫 번째 패턴을 찾은 다음, 관련된 코드 블록을 실행합니다. 구문적으로 match
문은 하나의 표현식과 일련의 case
절(case clauses)을 취하며, 각 case
절은 패턴과 코드 블록으로 구성됩니다.
case
절은 코드 블록을 포함하므로, match
문 자체도 복합문(compound statement)이 됩니다. match <expr>: <case_clause>+
와 같은 구문 구조를 따릅니다.
match
를 표현식(expression)으로 만드는 대신 문장(statement)으로 만들자는 제안도 있었으나, Python의 문장 지향적(statement-oriented) 특성과 잘 맞지 않고, 지나치게 길고 복잡한 표현식과 새로운 구문 구조의 필요성을 야기하여 거부되었습니다.
match
를 하드 키워드(hard keyword)로 만들지 않고 소프트 키워드(soft keyword)로 유지하기로 결정했습니다. match
는 기존 코드에서 흔히 사용되는 이름이므로, 하드 키워드로 만들 경우 거의 모든 기존 프로그램을 망가뜨리고 많은 사람에게 코드 수정 부담을 줄 것이기 때문입니다.
case
절에 as
나 |
대신 case
키워드를 사용하기로 했습니다. Python은 Algol의 전통을 따르는 문장 지향 언어이며, 각 복합문은 식별 키워드로 시작하므로 case
가 Python의 스타일과 전통에 가장 부합한다고 판단했습니다.
매치 의미론 (Match Semantics)
여러 case
절이 주어진 대상과 일치할 수 있으므로, “먼저 일치하는 규칙(first-to-match rule)”이 적용되어 대상에 대한 case
절의 선택이 모호하지 않도록 보장합니다. 또한, case
절은 점점 더 일반적인 패턴을 가질 수 있으며, 이 규칙은 가장 정밀한 패턴이 선택될 수 있도록 합니다 (프로그래머가 case
절을 올바르게 정렬할 책임이 있습니다).
패턴 매칭 구현에서 각 case
절이 자체적인 별도의 스코프(scope)를 설정하는 경우가 많지만, Python에서는 각 case
절이 주변 스코프의 변수에 직접 접근할 수 없는 별도의 함수가 되는 것과 같아 직관적이지 않습니다. 따라서 Python에서는 변수 바인딩이 해당 case
또는 match
문을 벗어나도 유효하도록 합니다. 이는 for
루프 및 with
문과 같은 기존 Python 구조와 일치합니다.
가드 (Guards)
일부 제약 조건은 패턴만으로는 적절하게 표현될 수 없습니다. 예를 들어, ‘보다 작음’ 또는 ‘보다 큼’ 관계는 패턴의 일반적인 ‘같음’ 의미론을 따르지 않습니다. 또한, 다른 서브패턴들은 독립적이어서 서로를 참조할 수 없습니다. 가드(guards)의 추가는 이러한 제한 사항을 해결합니다. 가드는 패턴에 첨부된 임의의 표현식이며, 패턴이 성공하려면 ‘참(truthy)’ 값으로 평가되어야 합니다.
예를 들어, case [x, y] if x < y:
는 가드(if x < y
)를 사용하여 두 개의 분리된 캡처 패턴 x
와 y
사이의 ‘보다 작음’ 관계를 표현합니다.
개념적인 관점에서 패턴은 대상에 대한 구조적 제약 조건을 선언적 스타일로 기술하며, 이상적으로는 부작용이 없습니다. 가드는 case
블록을 임의의 표현식(부작용이 있을 수 있음)으로 고도로 제어된 방식으로 향상시킵니다. 전체 기능을 정적 구조 부분과 동적으로 평가되는 부분으로 분리하는 것은 가독성을 높일 뿐만 아니라 컴파일러 최적화에 대한 엄청난 잠재력을 도입할 수 있습니다. 이러한 명확한 분리를 유지하기 위해 가드는 case
절 수준에서만 지원되며 개별 패턴에는 지원되지 않습니다.
패턴 (Patterns)
패턴은 두 가지 목적을 수행합니다. 대상에 (구조적) 제약 조건을 부과하고, 대상에서 어떤 데이터 값을 추출하여 변수에 바인딩해야 하는지 지정합니다. 패턴은 할당 대상(iterable unpacking에서처럼)과 두 가지 방식으로 다릅니다. 첫째, 대상의 구조에 추가적인 제약 조건을 부과하고, 둘째, 대상이 특정 패턴과 일치하지 않아도 안전하게 실패할 수 있습니다 (iterable unpacking에서는 오류로 간주됨). 후자는 패턴이 가능한 한 부작용을 피해야 한다는 것을 의미합니다.
AS 패턴 (AS Patterns)
대부분의 패턴은 대상이 충족해야 하는 (구조적) 제약 조건을 부과하는 반면, 캡처 패턴(capture pattern)은 대상의 구조나 실제 값에 관계없이 대상을 이름에 바인딩합니다. 결과적으로 패턴은 제약 조건을 표현하거나 값을 바인딩할 수 있지만, 둘 다 할 수는 없습니다. AS 패턴은 일반 패턴을 지정하고 변수에 대상을 캡처할 수 있도록 하여 이 간극을 메웁니다.
초기 버전에서는 AS 패턴이 case [first:=int(), second:=int()]
와 같이 ‘Walrus pattern’으로 고안되었지만, as
를 사용하는 것이 :=
보다 몇 가지 이점을 제공하여 as
가 채택되었습니다. as
는 import foo as bar
또는 except E as err:
에서와 같이 어떤 형태의 ‘처리’를 나타내는 반면, walrus 연산자는 표현식의 결과를 캡처하는 데 사용되기 때문입니다.
OR 패턴 (OR Patterns)
OR 패턴은 ‘구조적으로 동등한’ 대안들을 새로운 패턴으로 결합할 수 있도록 합니다. 즉, 여러 패턴이 공통 핸들러를 공유할 수 있습니다. OR 패턴의 서브패턴 중 하나라도 대상과 일치하면 전체 OR 패턴이 성공합니다.
정적 타입 언어는 변수 타입과 관련된 잠재적 충돌 때문에 OR 패턴 내에서 이름 바인딩(캡처 패턴)을 금지합니다. 동적 타입 언어인 Python은 덜 제한적이며 OR 패턴 내에서 캡처 패턴을 허용할 수 있습니다. 그러나 잠재적으로 정의되지 않은 이름을 남기지 않기 위해 각 서브패턴은 동일한 변수 집합을 바인딩해야 합니다.
대안들을 분리하기 위해 bar 심볼 |
를 사용할지 or
키워드를 사용할지에 대한 논의가 있었습니다. |
는 OR 패턴을 지원하는 모든 프로그래밍 언어에서 선택된 심볼이며, Python의 정규 표현식에서도 사용됩니다.
AND 및 NOT 패턴 (AND and NOT Patterns)
AND 패턴(&
)이나 NOT 패턴(!
)을 추가할지 논의되었으나, 그 유용성이 명확하지 않고 구문을 더 복잡하게 만들 수 있다는 판단에 따라 추가되지 않았습니다. 기존의 딕셔너리, 객체, 시퀀스 매칭 의미론에는 암묵적인 ‘and’가 포함되어 있으며, 가드 조건으로 많은 사용 사례를 처리할 수 있습니다.
리터럴 패턴 (Literal Patterns)
리터럴 패턴은 대상의 타입이나 구조보다는 값에 제약 조건을 부과하는 편리한 방법입니다. 이를 통해 패턴 매칭을 사용하여 switch
문을 에뮬레이션할 수도 있습니다. 일반적으로 대상은 표준 동등성(x == y
)을 사용하여 리터럴 패턴과 비교됩니다.
하지만 None
, False
, True
의 세 싱글톤 패턴은 동등성(==
)이 아닌 동일성(is
)으로 일치하도록 규칙을 채택했습니다. 이는 True == 1
이 참이므로 case True:
가 1.0과 일치하는 것과 같은 사용자에게 혼란을 줄 수 있는 미묘한 버그를 방지하기 위함입니다.
캡처 패턴 (Capture Patterns)
캡처 패턴은 어떤 값이든 받아들이고 이를 (지역) 변수에 바인딩하는 이름 형태를 취합니다 (이름이 nonlocal
또는 global
로 선언되지 않는 한). 이러한 의미에서 캡처 패턴은 함수 정의의 매개변수와 유사합니다.
캡처 패턴에 사용되는 이름은 동일한 패턴 내의 다른 캡처 패턴과 일치해서는 안 됩니다. 이는 매개변수 목록 내에서 각 매개변수 이름이 고유해야 하는 것과 유사합니다.
와일드카드 패턴 (Wildcard Pattern)
와일드카드 패턴(_
)은 ‘캡처’ 패턴의 특별한 경우입니다. 어떤 값이든 받아들이지만, 변수에 바인딩하지 않습니다. 이 규칙의 의도는 패턴에서 와일드카드의 반복적인 사용을 지원하는 것입니다. (x, x)
는 오류이지만, (_, _)
는 유효합니다.
특히 더 큰 (시퀀스) 패턴에서 실제 의미가 있는 값에 집중하고 나머지는 무시할 수 있도록 하는 것이 중요합니다. 와일드카드가 없으면 바인딩되지만 사용되지 않는 수많은 지역 변수를 ‘발명’해야 할 필요가 생길 것입니다.
밑줄 _
을 와일드카드 패턴으로 선택한 것에 대한 많은 논의가 있었지만, 밑줄은 이미 이터러블 언패킹에서 ‘값을 무시하는’ 마커로 널리 사용되고 있습니다.
case _:
와 같이 단일 와일드카드 패턴을 갖는 case
블록은 변수에 바인딩하거나 다른 작업을 수행하지 않고 모든 대상을 받아들입니다. 이는 else:
와 의미론적으로 동일하지만, match
문 구문에 else
블록을 추가해도 다른 컨텍스트에서 와일드카드 패턴의 필요성이 사라지는 것은 아니므로 추가하지 않았습니다.
값 패턴 (Value Patterns)
파라미터 값에 명명된 상수를 사용하거나 특정 값의 의미를 명확히 하는 것은 좋은 프로그래밍 스타일입니다. 예를 들어, case (HttpStatus.OK, body):
가 case (200, body):
보다 선호될 것입니다. 여기서 발생하는 주요 문제는 캡처 패턴(변수 바인딩)과 값 패턴을 구별하는 방법입니다.
값 패턴은 엄밀히 말하면 필요하지 않지만, case (status, body) if status == HttpStatus.OK:
와 같이 가드를 사용하여 구현할 수 있습니다. 그럼에도 불구하고 값 패턴의 편리함은 의심의 여지가 없습니다.
상수는 대문자로 작성되거나 열거형과 유사한 네임스페이스에 수집되는 경향이 있다는 관찰은 상수를 구문적으로 식별하는 가능한 규칙을 제시합니다. 우리는 점으로 구분된 이름(즉, 속성 접근)을 값 패턴으로 해석하는 규칙만을 채택했습니다 (예: HttpStatus.OK
).
그룹 패턴 (Group Patterns)
사용자가 명시적으로 그룹화를 지정할 수 있도록 하는 것은 특히 OR 패턴의 경우에 유용합니다.
시퀀스 패턴 (Sequence Patterns)
시퀀스 패턴은 이미 확립된 이터러블 언패킹의 구문과 의미론을 최대한 따릅니다. 서브패턴은 할당 대상(변수, 속성, 서브스크립트)을 대체합니다. 또한 시퀀스 패턴은 신중하게 선택된 가능한 대상 집합하고만 일치하는 반면, 이터러블 언패킹은 모든 이터러블에 적용될 수 있습니다.
이터러블 언패킹과 마찬가지로 ‘튜플’과 ‘리스트’ 표기법을 구별하지 않습니다. [a, b, c]
, (a, b, c)
, a, b, c
는 모두 동일합니다.
별표가 붙은 패턴(*args
등)은 이터러블 언패킹과 마찬가지로 임의 길이의 서브 시퀀스를 캡처합니다. 시퀀스 패턴에는 하나의 별표 항목만 있을 수 있습니다.
시퀀스 패턴은 모든 이터러블 객체를 순회하지 않습니다. 모든 요소는 서브스크립트(subscripting)와 슬라이싱(slicing)을 통해 접근되며, 대상은 collections.abc.Sequence
의 인스턴스여야 합니다. 여기에는 물론 리스트와 튜플이 포함되지만, 집합(sets)과 딕셔너리(dictionaries)는 제외됩니다. 문자열과 바이트는 이중적인 특성을 가지므로 시퀀스 패턴으로 일치하지 않습니다.
매핑 패턴 (Mapping Patterns)
딕셔너리 또는 일반적인 매핑은 Python에서 가장 중요하고 널리 사용되는 데이터 구조 중 하나입니다. 시퀀스와 달리 매핑은 키로 식별되는 임의의 요소에 빠르게 직접 접근할 수 있도록 만들어졌습니다.
매핑 패턴은 딕셔너리 조회(lookup)의 일반적인 사용법을 반영합니다. 사용자가 상수/알려진 키를 사용하여 매핑에서 일부 값을 추출하고, 그 값들을 주어진 서브패턴과 일치시킬 수 있도록 합니다. **rest
가 없더라도 대상의 추가 키는 무시됩니다. 이는 추가 항목이 일치를 실패하게 하는 시퀀스 패턴과는 다릅니다. 그러나 매핑은 실제로 시퀀스와 다릅니다.
과도하게 비용이 많이 드는 매칭 알고리즘을 피하기 위해 키는 리터럴이거나 값 패턴이어야 합니다.
get(key, default)
를 사용하는 것이 __getitem__(key)
호출 후 AttributeError
를 확인하는 것보다 미묘한 이유가 있습니다. 대상이 defaultdict
인 경우, 존재하지 않는 키에 대해 __getitem__
을 호출하면 해당 키가 추가되는 예상치 못한 부작용이 발생할 수 있습니다. get()
을 사용하면 이러한 부작용을 피할 수 있습니다.
클래스 패턴 (Class Patterns)
클래스 패턴은 두 가지 목적을 수행합니다. 주어진 대상이 특정 클래스의 인스턴스인지 확인하고, 대상의 특정 속성에서 데이터를 추출하는 것입니다. isinstance()
는 Python 프로그램에서 정적으로 가장 자주 사용되는 함수 중 하나입니다. 이러한 인스턴스 검사는 일반적으로 객체에 저장된 정보에 대한 후속 접근이나 가능한 조작에 선행합니다.
클래스 패턴은 인스턴스 검사와 관련 속성(추가 제약 조건 가능)을 간결하게 지정할 수 있도록 합니다. 일반적인 Python 객체를 다룰 때는 잠재적으로 매우 많은 수의 순서 없는 속성에 직면합니다. Node(left=x, right=y)
와 같이 관심 있는 속성을 명시적으로 지정하거나, __match_args__
필드를 통해 속성의 순서를 지정하여 위치 서브패턴을 사용할 수 있습니다.
클래스 패턴의 구문은 구성(construction)을 반영한다는 아이디어에 기반합니다. 이는 할당 대상, 함수 정의 또는 이터러블 언패킹 등 거의 모든 Python 구문에서 이미 그렇습니다.
패턴 변수에 대한 타입 어노테이션(Type annotations)을 패턴과 결합하려는 제안은 구문 모호성과 제네릭 타입에 대한 isinstance()
검사의 제한으로 인해 많은 문제가 있어 거부되었습니다.
역사 및 맥락 (History and Context)
패턴 매칭은 1970년대 후반 튜플 언패킹의 형태로, 그리고 연결 리스트나 트리와 같은 재귀적 데이터 구조를 처리하는 수단으로 등장했습니다. 초기 패턴 매칭 지지자들은 구조화된 데이터를 C의 struct
나 나중에 도입된 객체보다는 ‘태그가 지정된 튜플(tagged tuples)’로 구성했습니다.
패턴 매칭을 사용하여 재귀적 데이터 구조를 처리하는 개념은 즉시 패턴 매칭을 사용하여 더 일반적인 재귀 ‘패턴’을 처리하는 아이디어로 이어졌습니다.
패턴 매칭이 새롭게 등장하는 프로그래밍 언어에 반복적으로 통합되면서 구문은 약간 진화하고 확장되었습니다. |
는 대안 패턴을 나타내고, 밑줄 _
는 와일드카드로 널리 채택되었습니다. 밑줄은 이미 Python의 이터러블 언패킹에서 동등한 용량으로 자주 사용되고 있으므로 이러한 보편적인 표준을 유지했습니다.
객체 지향 프로그래밍은 추상화와 캡슐화를 강조하면서 패턴 매칭에 심각한 도전을 제기했습니다. 요컨대, 객체 지향 프로그래밍에서는 더 이상 객체를 태그가 지정된 튜플로 볼 수 없습니다.
이러한 도전에 대처하기 위해 패턴은 원래의 튜플 생성자와 점점 더 독립적이게 되었습니다. Node(left, right)
와 같은 패턴에서 Node
는 더 이상 수동적인 태그가 아니라, 주어진 객체가 올바른 구조를 가지고 있는지 적극적으로 확인하고 left
및 right
필드를 추출할 수 있는 함수가 됩니다.
Python에서는 isinstance()
와 클래스의 __match_args__
필드를 함께 사용하여 객체가 올바른 구조를 가지고 있는지 확인한 다음, 일부 속성을 튜플로 변환합니다. Python의 동적 특성인 ‘덕 타이핑(duck typing)’을 존중하여 특정 속성의 존재 또는 제약 조건을 지정하는 더 직접적인 방법도 추가했습니다. Node(x, y)
대신 object(left=x, right=y)
를 작성할 수 있습니다.
하위 호환성 (Backwards Compatibility)
“소프트 키워드(soft keywords)”와 새로운 PEG 파서(PEP 617)를 사용함으로써, 이 제안은 완벽하게 하위 호환성을 유지합니다. 그러나 Python 소스 코드를 파싱하기 위해 LL(1) 파서를 사용하는 서드파티 도구는 이러한 기능을 지원하기 위해 파서 기술을 전환해야 할 수도 있습니다.
보안 영향 (Security Implications)
이 언어 기능으로 인한 보안 영향은 없을 것으로 예상됩니다.
참고 구현 (Reference Implementation)
GitHub에서 완전한 기능을 갖춘 CPython 구현을 사용할 수 있습니다. 위 구현을 기반으로 한 대화형 플레이그라운드는 Binder와 Jupyter를 사용하여 생성되었습니다.
참고 자료 (References)
- Kohn et al., Dynamic Pattern Matching with Python.
- Binder
- Jupyter
저작권 (Copyright)
이 문서는 공개 도메인 또는 CC0-1.0-Universal 라이선스(둘 중 더 허용적인 라이선스) 하에 제공됩니다.
⚠️ 알림: 이 문서는 AI를 활용하여 번역되었으며, 기술적 정확성을 보장하지 않습니다. 정확한 내용은 반드시 원문을 확인하시기 바랍니다.
Comments