Python 3.10 パターンマッチ構文導入予定


パターンマッチ式

Python3.10では、他言語のswitch(文・式)にあたる、
パターンマッチ構文が導入される予定です。
公式ドキュメントの記述を元にした要約を、
この記事ではお伝えします。
(2021/04/07時点のドキュメントに基づいており、
最終リリースが出るまで、仕様は確定していません。)

構文概略

パターンマッチ構文の構文概略です。
どの条件にも当てはまらない事例を定義するために、
_が使われています。
(switch文・式で使われるdefaultではないことに注意です。)

条件は上から下に評価され、
当てはまる条件に紐づいた操作が実行されます。
何も該当する条件がない場合、何も処理は実行されません。

match subject:
    case <pattern_1>:
        <action_1>
    case <pattern_2>:
        <action_2>
    case <pattern_3>:
        <action_3>
    case _:
        <action_wildcard>

if文との違い 宣言的か手続き的か

パターンマッチ構文は他言語におけるswitch文・式に対応します。
今までPythonには、if_elif_elseというif節と、3項演算子しかありませんでした。
複雑な条件分岐を読み手に分かりやすく伝えるために、
今回パターンマッチ構文が導入されました。

パターンマッチ構文とリテラル

以下の関数では、
パターンマッチ構文を使い、
ステータスコードが、整数リテラルと照合されています。
合致した事例に対応するエラーメッセージがかえされます。

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"

同一の結果をもたらす複数の値を、|を用いてまとめて書くことができます。
以下のコードでは、401または403または404の時、”Not allowed”が返されます。

case 401 | 403 | 404:
    return "Not allowed"

ワイルドカードなしでの振る舞い

最後の_(ワイルドカード)がない場合、
どの事例にも当てはまらないstatus(例えば500)は、
何も値を返しません。
そのまま関数が終了します。

def http_error(status):
    match status:
        case 400:
            return "Bad request"
        case 404:
            return "Not found"
        case 418:
            return "I'm a teapot"

パターンマッチ構文におけるリテラルと変数

パターンマッチには、事前に定義された、変数を使うこともできます。
以下の事例では、リテラルの代わりに、xやyという変数が使われています。

# 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")

パターンマッチ構文とクラスオブジェクト

コンストラクターに似た構文で、
クラスオブジェクトのプロパティの値に対する、
パターンマッチをおこなうことができます。

class Point:
    x: int
    y: int

def location(point):
    match point:
        case Point(x=0, y=0):
            print("Origin is the point's location.")
        case Point(x=0, y=y):
            print(f"Y={y} and the point is on the y-axis.")
        case Point(x=x, y=0):
            print(f"X={x} and the point is on the x-axis.")
        case Point():
            print("The point is located somewhere else on the plane.")
        case _:
            print("Not a point")

位置引数とパターンマッチ構文の組み合わせ

クラスオブジェクトをパターンマッチ構文の評価対象にする際、
位置引数を使用することも可能です。

Point(1, var)
Point(1, y=var)
Point(x=1, y=var)
Point(y=var, x=1)

パターンマッチ構文と配列

以下のコードのように、
パターンマッチ構文の条件には、
配列も使用できます。

match points:
    case []:
        print("No points in the list.")
    case [Point(0, 0)]:
        print("The origin is the only point in the list.")
    case [Point(x, y)]:
        print(f"A single point {x}, {y} is in the list.")
    case [Point(0, y1), Point(0, y2)]:
        print(f"Two points on the Y axis at {y1}, {y2} are in the list.")
    case _:
        print("Something else is found in the list.")

複雑なパターンマッチとワイルドカード

ワイルドカードを、
tupleの一部として使うことができます。
例として、(‘error’, code, _)のようにです。
以下のコードでは、最後のワイルドカードを
使った事例は、
test_variableが(‘error’, code, 100) でも、
(‘error’, code, 800)でも動作します。

match test_variable:
    case ('warning', code, 40):
        print("A warning has been received.")
    case ('error', code, _):
        print(f"An error {code} occured.")

ガード付帯条件

if節をcase節の後に付帯条件としてつけることで、より詳細な条件指定ができます。

match point:
    case Point(x, y) if x == y:
        print(f"The point is located on the diagonal Y=X at {x}.")
    case Point(x, y):
        print(f"Point is not on the diagonal.")

Enum型とパターンマッチ

Enumもパターンマッチの条件に使用可能です。

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 :(")
  • このエントリーをはてなブックマークに追加

PAGE TOP