Python의 구조 패턴 일치

Mehvish Ashiq 2023년10월10일
  1. 구조 패턴 일치 소개 및 그 중요성
  2. Python에서 구조 패턴 일치 사용
Python의 구조 패턴 일치

Python 3.10 이전에는 다른 프로그래밍 언어에서 스위치 케이스라고 하는 구조적 패턴 일치를 사용하는 내장 방법이 없었습니다. Python 3.10 릴리스부터 match ... case 문을 사용하여 switch ... case 문을 에뮬레이트할 수 없습니다.

이 튜토리얼은 Python에서 구조적 패턴 일치와 그 중요성을 소개합니다. 또한 match ... case 문을 사용하여 시연하기 위해 다양한 패턴을 사용합니다.

구조 패턴 일치 소개 및 그 중요성

2021년 초부터 출시된 Python 3.9 이하 버전에서는 match 키워드를 사용할 수 없습니다. 당시 우리는 사전이나 중첩된 if/elif/else 문을 사용하여 switch ... case를 시뮬레이션하는 데 익숙했습니다.

그러나 Python 3.10에는 구조적 패턴 일치(match ... case 문)라는 새로운 기능이 도입되었습니다. 이는 Java, C++ 및 기타 여러 프로그래밍 언어에서 사용하는 switch ... case 문과 동일합니다.

이 새로운 기능을 통해 간단하고 읽기 쉬우며 오류 발생 가능성이 최소화된 흐름 제어 문을 작성할 수 있습니다.

Python에서 구조 패턴 일치 사용

구조적 패턴 일치는 switch ... case 문으로 사용되며 이보다 더 강력합니다. 어떻게? 아래의 몇 가지 예를 살펴보고 다양한 상황에서 사용하는 방법을 알아보겠습니다.

match ... case 문의 기본 사용

예제 코드:

# >= Python 3.10
colour = "blue"
match colour:
    case "green":
        print("The specified colour is green")
    case "white":
        print("Wow, you've picked white")
    case "green":
        print("Great, you are going with green colour")
    case "blue":
        print("Blue like sky...")

출력:

Blue like sky...

여기에서 먼저 blue를 포함하는 color 변수가 있습니다. 그런 다음 match 키워드를 사용합니다. 이 키워드는 color 변수의 값을 다양한 지정된 사례와 일치시킵니다. 각 사례는 case 키워드로 시작하여 비교하거나 확인하려는 패턴이 이어집니다.

패턴은 다음 중 하나일 수 있습니다.

  • 리터럴 패턴
  • 캡처 패턴
  • 와일드카드 패턴
  • 상수 값 패턴
  • 시퀀스 패턴
  • 매핑 패턴
  • 클래스 패턴
  • OR 패턴
  • 해마 패턴

match ... case 문은 일치하는 첫 번째 case 아래의 코드만 실행합니다.

일치하는 case가 없으면 어떻게 됩니까? 사용자는 그것에 대해 어떻게 알 수 있습니까? 이를 위해 다음과 같이 기본 case를 가질 수 있습니다.

예제 코드:

# >= Python 3.10
colour = "yellow"
match colour:
    case "green":
        print("The specified colour is green")
    case "white":
        print("Wow, you've picked white")
    case "green":
        print("Great, you are going with green colour")
    case "blue":
        print("Blue like sky...")
    case other:
        print("No match found!")

출력:

No match found!

match ... case를 사용하여 데이터 구조 감지 및 분해

예제 코드:

# >= Python 3.10
student = {"name": {"first": "Mehvish", "last": "Ashiq"}, "section": "B"}

match student:
    case {"name": {"first": firstname}}:
        print(firstname)

출력:

Mehvish

위의 예에서 구조적 패턴 일치는 다음 두 줄의 코드에서 작동합니다.

# >= Python 3.10
match student:
    case {"name": {"first": firstname}}:

match ... case 문을 사용하여 student 데이터 구조에서 추출하여 학생의 이름을 찾습니다. 여기서 학생은 학생의 정보를 담고 있는 사전입니다.

case 줄은 student와 일치하는 패턴을 지정합니다. 위의 예를 고려하여 값이 새 사전인 "이름" 키가 있는 사전을 찾습니다.

이 중첩된 사전에는 값이 firstname 변수에 바인딩된 "first" 키가 포함되어 있습니다. 마지막으로 firstname 변수를 사용하여 값을 인쇄합니다.

더 깊이 관찰하면 여기에서 매핑 패턴을 배웠습니다. 어떻게? 매핑 패턴은 {"student": s, "emails": [*es]}와 같으며 적어도 지정된 키 집합과 매핑을 일치시킵니다.

모든 하위 패턴이 해당 값과 일치하면 키에 해당하는 값과 일치하는 동안 하위 패턴 바인딩이 무엇이든 바인딩합니다. 추가 항목 캡처를 허용하려면 패턴 끝에 **rest를 추가할 수 있습니다.

캡처 패턴 및 시퀀스 패턴과 함께 match ... case 사용

예제 코드:

# >= Python 3.10
def sum_list_of_numbers(numbers):
    match numbers:
        case []:
            return 0
        case [first, *rest]:
            return first + sum_list_of_numbers(rest)


sum_list_of_numbers([1, 2, 3, 4])

출력:

10

여기에서 재귀 함수를 사용하여 캡처 패턴을 사용하여 지정된 패턴에 대한 일치 항목을 캡처하고 이름에 바인딩합니다.

이 코드 예제에서 첫 번째 case는 빈 목록과 일치하는 경우 합계로 0을 반환합니다. 두 번째 case는 두 개의 캡처 패턴이 있는 시퀀스 패턴을 사용하여 목록을 여러 항목/요소 중 하나와 일치시킵니다.

여기에서 목록의 첫 번째 항목이 캡처되고 first 이름에 바인딩되는 반면 두 번째 캡처 패턴인 *rest는 압축 풀기 구문을 사용하여 항목/요소의 수와 일치합니다.

rest는 첫 번째 항목을 제외하고 숫자의 모든 항목/요소가 있는 목록에 바인딩됩니다. 출력을 얻기 위해 위에서 주어진 숫자 목록을 전달하여 sum_list_of_numbers() 함수를 호출합니다.

와일드카드 패턴과 함께 match ... case 사용

예제 코드:

# >= Python 3.10
def sum_list_of_numbers(numbers):
    match numbers:
        case []:
            return 0
        case [first, *rest]:
            return first + sum_list_of_numbers(rest)
        case _:
            incorrect_type = numbers.__class__.__name__
            raise ValueError(
                f"Incorrect Values. We Can only Add lists of numbers,not {incorrect_type!r}"
            )


sum_list_of_numbers({"1": "2", "3": "4"})

출력:

ValueError: Incorrect Values. We Can only Add lists of numbers, not 'dict'

우리는 match ... case 문의 기본 사용법을 배우면서 와일드카드 패턴 사용의 개념을 배웠지만 와일드카드 패턴 용어를 소개하지 않았습니다. 처음 두 사례가 일치하지 않는 시나리오를 상상해 보십시오. 최종 사례로 포괄 패턴이 있어야 합니다.

예를 들어 목록 대신 다른 유형의 데이터 구조를 얻으면 오류를 발생시키고 싶습니다. 여기에서 _를 와일드카드 패턴으로 사용할 수 있으며 이름에 바인딩하지 않고 모든 항목과 일치합니다. 사용자에게 알리기 위해 이 마지막 사례에 오류 처리를 추가합니다.

당신은 무엇을 말합니까? 우리의 패턴이 잘 어울리나요? 다음과 같이 문자열 값 목록을 전달하여 sum_list_of_numbers() 함수를 호출하여 테스트해 보겠습니다.

sum_list_of_numbers(["1", "2", "3", "4"])

다음 오류가 발생합니다.

TypeError: can only concatenate str (not "int") to str

따라서 패턴이 여전히 완벽하지 않다고 말할 수 있습니다. 왜? 목록 유형 데이터 구조를 sum_list_of_numbers() 함수에 전달하지만 예상한 int 유형이 아닌 문자열 유형 값을 갖기 때문입니다.

해결 방법은 다음 섹션을 참조하십시오.

클래스 패턴과 함께 match ... case 사용

예제 코드:

# >= Python 3.10
def sum_list_of_numbers(numbers):
    match numbers:
        case []:
            return 0
        case [int(first), *rest]:
            return first + sum_list_of_numbers(rest)
        case _:
            raise ValueError(f"Incorrect values! We can only add lists of numbers")


sum_list_of_numbers(["1", "2", "3", "4"])

출력:

ValueError: Incorrect values! We can only add lists of numbers

기본 사례(첫 번째 case)는 0을 반환합니다. 따라서 합산은 숫자로 추가할 수 있는 유형에 대해서만 작동합니다. Python은 텍스트 문자열과 숫자를 추가하는 방법을 모릅니다.

따라서 클래스 패턴을 사용하여 정수만 일치하도록 패턴을 제한할 수 있습니다. 클래스 패턴은 매핑 패턴과 유사하지만 키 대신 속성을 일치시킵니다.

OR 패턴과 함께 match ... case 사용

예제 코드:

# >= Python 3.10
def sum_list_of_numbers(numbers):
    match numbers:
        case []:
            return 0
        case [int(first) | float(first), *rest]:
            return first + sum_list_of_numbers(rest)
        case _:
            raise ValueError(f"Incorrect values! We can only add lists of numbers")

sum_list_of_numbers() 함수가 int 유형이든 float 유형이든 상관없이 값 목록에 대해 작동하도록 만들고 싶다고 가정합니다. 파이프 기호(|)로 표시되는 OR 패턴을 사용합니다.

위의 코드는 지정된 목록에 int 또는 float 유형 값 이외의 값이 포함된 경우 ValueError를 발생시켜야 합니다. 아래 세 가지 시나리오를 모두 고려하여 테스트해 보겠습니다.

테스트 1: int 유형 값이 있는 목록을 전달합니다.

sum_list_of_numbers([1, 2, 3, 4])  # output is 10

테스트 2: float 유형 값이 있는 목록을 전달합니다.

sum_list_of_numbers([1.0, 2.0, 3.0, 4.0])  # output is 10.0

테스트 3: int 및 float 유형을 제외한 다른 유형의 목록을 전달합니다.

sum_list_of_numbers(["1", "2", "3", "4"])
# output is ValueError: Incorrect values! We can only add lists of numbers

보시다시피 sum_list_of_numbers() 함수는 OR 패턴을 사용하기 때문에 int 및 float 유형 값 모두에 대해 작동합니다.

리터럴 패턴과 함께 match ... case 사용

예제 코드:

# >= Python 3.10
def say_hello(name):
    match name:
        case "Mehvish":
            print(f"Hi, {name}!")
        case _:
            print("Howdy, stranger!")


say_hello("Mehvish")

출력:

Hi, Mehvish!

이 예제에서는 match ... case 문의 기본 사용법을 학습하는 동안 이미 수행한 것처럼 명시적 숫자 또는 문자열과 같은 리터럴 객체와 일치하는 리터럴 패턴을 사용합니다.

가장 기본적인 유형의 패턴이며 Java, C++ 및 기타 프로그래밍 언어와 유사한 switch ... case 문을 시뮬레이션할 수 있습니다. 페이지를 방문하여 모든 패턴에 대해 알아볼 수 있습니다.

Mehvish Ashiq avatar Mehvish Ashiq avatar

Mehvish Ashiq is a former Java Programmer and a Data Science enthusiast who leverages her expertise to help others to learn and grow by creating interesting, useful, and reader-friendly content in Computer Programming, Data Science, and Technology.

LinkedIn GitHub Facebook