match-case【match文】3.10
メモ ( 概要 ソフトキーワード ) 構文 ( 各種パターン ) 例
メモ
概要
〔 例 〕- C言語系の switch 文の拡張
- 整数値だけでなく、各種型と比較可能
- 複数パターン(or)の指定可 (case の連続相当)
- break 等はなし (後のパターンの処理はなし)
- default: 相当は、case _: (アンダースコア)
- 他言語のパターンマッチング文相当
- 各種パターンと比較可能
- キャプチャが可能 (キャプチャ パターン)
ソフトキーワード
〔 例 〕- match と case は、ソフトキーワード
- match-case として使用される場合のみキーワード、その他では自由に使用可
- ワイルドカード パターン での _ (アンダースコア)もソフトキーワード
関連
外部リンク
構文
match マッチ対象式:
case パターン1 [if ガード条件1]:処理1 (1行)
処理1 (複数行可)
・・・
case パターンn [if ガード条件n]:処理n (1行)
処理n (複数行可)
[case _:default 処理 (1行)
default 処理 (複数行可)]
マッチ対象式マッチ対象
カンマ区切り:tuple【タプル型】となるので、パターンnも合わせる
パターンn各種パターン の指定
複数 (OR パターン)サブパターン1|・・・|サブパターンn
(キャプチャ変数:サブパターン内では重複不可・サブパターン間では同じ指定が必要)
キャプチャサブパターンn as キャプチャ名n
ガード条件n
パターンn全体に対する制約
処理n (1行)・処理n (複数行可)
パターンnにマッチする場合の処理、どちらか1つを指定
default 処理 (1行)・default 処理 (複数行可)
どのパターンにもマッチしない場合の処理、どちらか1つを指定 (キャプチャは不可)
※:正式な構文は、The match statement を参照
各種パターン
(サブ)パターンn | 備考 |
---|---|
リテラル パターン〔 例 〕 | |
符号付き数値 | |
複素数 | |
文字列 | f-string【フォーマット済み文字列リテラル】は不可 |
None | |
True False | |
キャプチャ パターン〔 例 〕 | |
変数 (ワイルドカードのみを除く) | 変数に値をバインド |
ワイルドカード パターン〔 例 〕 ( _ はソフトキーワード:パターン内でのみキーワード) | |
_ (アンダースコア) を含むパターン | 該当位置と常にマッチ (参照不可) |
_ (アンダースコア) のみ | 常にマッチ (構文にも記述) (最後にのみ指定可・参照不可) |
値 パターン〔 例 〕 | |
~.name | 名前付き定数 (列挙型等) |
グループ パターン | |
(パターン) | カッコで強調させるだけ |
シーケンス パターン〔 例 〕 | |
(A) [要素 (複数:カンマ区切り)] (B) (要素 (複数:カンマ区切り)) (C) 要素 (カンマ区切り) (タプル:1要素でもカンマが必要) | (A) list【リスト型】 (B) tuple【タプル型】 (C) タプル型のカッコ省略形 要素: *でアンパック相当 (アンパック:各型の詳細参照) | で or 指定可 as でキャプチャ可 |
マッピング パターン〔 例 〕 | |
{キー1:値1, ・・・[, **キャプチャ変数]} | dict【辞書型】 指定キー以外は無視 重複キーはエラー ** で残り部分のキャプチャ可 (最後にのみ指定可 / **_は不可) |
クラス パターン〔 例 〕 | |
クラス名([位置引数 (カンマ区切り)][,][キーワード引数 (カンマ区切り)]) コンストラクタの記述相当 | 型チェック キャプチャ可 __match_args__ 属性 3.10 指定で位置引数が可能 マッピング パターン 内での使用も可 |
例
基本
def func_match(subject):
match subject:
case 1:
# 単独
print('(int-A) 1')
case 3 | 4 | 5:
# 複数
print('(int-B) (3 | 4 | 5)')
case 7 | 8 | 9 as x:
# 複数+キャプチャ
print(f'(int-C) {x} (7 | 8 | 9)')
case 'string':
# 単独 (文字列)
print('(str)')
case [2, 3, 4]:
# list
print('(list-A) ([2, 3, 4])')
case [x, y, z] as full if x < y < z:
# list+キャプチャ+ガード
print(f'(list-B) [{x}, {y}, {z}] {full} (x < y < z)')
case [x, y, z] as full:
# list+キャプチャ
print(f'(list-C) [{x}, {y}, {z}] {full}')
case x if x < 0:
# キャプチャ+ガード
print(f'(int) {x} (x < 0)')
case _:
print('(Z) その他')
func_match(1) # 出力:(int-A) 1
func_match(4) # 出力:(int-B) (3 | 4 | 5)
func_match(8) # 出力:(int-C) 8 (7 | 8 | 9)
func_match('string') # 出力:(str)
func_match([2, 3, 4])
# 出力:(list-A) ([2, 3, 4])
func_match([12, 34, 56])
# 出力:(list-B) [12, 34, 56] [12, 34, 56] (x < y < z)
func_match([12, 345, 67])
# 出力:(list-C) [12, 345, 67] [12, 345, 67]
func_match(-9) # 出力:(int) -9 (x < 0)
func_match(10) # 出力:(Z) その他
ソフトキーワード
def match_softkeyword(subject, match=None, case=None):
match subject:
case 1:
print(f'(A) 1 {match = } {case = }')
match = 123
case = 456
print(f'{match = } {case = }')
case 2:
print('(B) 2')
case _:
print('(Z) その他')
match_softkeyword(1, 23, 45)
# 出力:(A) 1 match = 23 case = 45
# 出力:match = 123 case = 456
match_softkeyword(2)
# 出力:(B) 2
match_softkeyword(3)
# 出力:(Z) その他
match = 456
case = 789
print(f'{match = } {case = }')
# 出力:match = 456 case = 789
リテラル パターン
def match_literal(subject):
match subject:
case 10:
print('(int-A) [10]')
#case (12 * 34): # SyntaxError
# pass
case 20:
print('(int-B) [20]')
case 30 | 40 | 50:
# 複数指定
print('(int-C) [30 | 40 | 50]')
case 60 | 70 | 80 as n:
# 複数指定+
print(f'(int-D) {n} [60 | 70 | 80]')
case -123.456 as f:
print(f'(float-A) {f}')
case 1 + 2j:
print('(complex-A) [1 + 2j]')
case -3 - 4j as c:
print(f'(complex-B) {c}')
case 'string':
print('(str-A) [string]')
case '''String''' as s:
print(f'(str-B) {s}')
case r'STRING' as s:
print(f'(str-C) {s}')
#case f'STRING': # SyntaxError
# pass
case None:
print('(None)')
case True as b:
print(f'(bool) {b}')
case False as b:
print(f'(bool) {b}')
case n if n % 9 == 0:
print(f'(guard) {n} [9xN]')
case _:
print('(Z) その他')
match_literal(10) # 出力:(int-A) [10]
match_literal(20) # 出力:(int-B) [20]
match_literal(40) # 出力:(int-C) [30 | 40 | 50]
match_literal(70) # 出力:(int-D) 70 [60 | 70 | 80]
match_literal(-123.456) # 出力:(float-E) -123.456
match_literal(-123) # 出力:(Z) その他
match_literal(1 + 2j) # 出力:(complex-A) [1 + 2j]
match_literal(-3 - 4j) # 出力:(complex-B) (-3-4j)
match_literal('string') # 出力:(str-A) [string]
match_literal('String') # 出力:(str-B) String
match_literal('''String''') # 出力:(str-B) String
match_literal('STRING') # 出力:(str-C) STRING
match_literal(r'STRING') # 出力:(str-C) STRING
match_literal(None) # 出力:(None)
match_literal(True) # 出力:(bool) True
match_literal(False) # 出力:(bool) False
match_literal(18) # 出力:(guard) 18 [9xN]
match_literal(27) # 出力:(guard) 27 [9xN]
match_literal(100) # 出力:(Z) その他
キャプチャ パターン
def match_capture(subject):
match subject:
case [x, 0]:
print(f'(A) [{x}, 0] ([x, 0])')
case [0, y]:
print(f'(B) [0, {y}] ([0, y])')
case [_, 100] | [100, _] as lst:
print(f'(C) {lst} ([_, 100] | [100, _])')
case [x, y] if x < y:
print(f'(D) [{x}, {y}] (x < y)')
case [x, y]:
print(f'(E) [{x}, {y}]')
case x:
print(f'(Z) {x}')
match_capture([123, 0]) # 出力:(A) [123, 0] ([x, 0])
match_capture([0, 456]) # 出力:(B) [0, 456] ([0, y])
match_capture([123, 100]) # 出力:(C) [123, 100] ([_, 100] | [100, _])
match_capture([100, 456]) # 出力:(C) [100, 456] ([_, 100] | [100, _])
match_capture([123, 456]) # 出力:(D) [123, 456] (x < y)
match_capture([123, -456]) # 出力:(E) [123, -456]
match_capture(123) # 出力:(Z) 123
match_capture('string') # 出力:(Z) string
ワイルドカード パターン
def match_wildcard(subject):
match subject:
case [x, _, 0]:
print(f'(A) [{x}, _, 0] ([x, _, 0])')
case [123, _, _]:
print(f'(B) ([123, _, _])')
case [1, _, _] as lst:
print(f'(C) {lst} ([1, _, _])')
#case [_, y, z] if _ < 0: # UnboundLocalError
# pass
case [x, y, _] if x < 0:
#print(_) # UnboundLocalError
print(f'(D) [{x}, {y}, _] ([x, y, _] if x < 0)')
_ = '変数D'
print(_)
case _:
print('(Z) その他')
match_wildcard([123, 456, 0]) # 出力:(A) [123, _, 0] ([x, _, 0])
match_wildcard([123, 456, 789]) # 出力:(B) ([123, _, _])
match_wildcard([1, 23, 456]) # 出力:(C) [1, 23, 456] ([1, _, _])
match_wildcard([-123, 456, 789])
# 出力:(D) [-123, 456, _] ([x, y, _] if x < 0)
# 出力:変数D
match_wildcard([100, 456, 789])
# 出力:(Z) その他
_ = '変数'
print(_)
# 出力:変数
値 パターン
from enum import Enum
class Color(Enum):
RED = 0xFF0000
GREEN = 0x00FF00
BLUE = 0x0000FF
BLACK = 0x000000
WHITE = 0xFFFFFF
GRAY = 0x808080
def match_enum(subject):
match subject:
case Color.RED:
print('Red')
case Color.GREEN:
print('Green')
case Color.BLUE:
print('Blue')
case Color.WHITE | Color.BLACK:
print('Black or White')
case _:
print('その他')
match_enum(Color.RED) # 出力:Red
match_enum(Color.GREEN) # 出力:Green
match_enum(Color.BLUE) # 出力:Blue
match_enum(Color.BLACK) # 出力:Black or White
match_enum(Color.WHITE) # 出力:Black or White
match_enum(Color.GRAY) # 出力:その他
シーケンス パターン
def match_sequence(subject):
match subject:
case [123, 456, 789] as seq:
print(f'(A) {seq} [123, 456, 789]')
case (123, 456, 789): # 上記 case と同等
print('(B) (123, 456, 789)')
case 123, 456, 789: # 上記 case と同等
print('(C) 123, 456, 789')
case [1, *yz]:
# アンパック相当
print(f'(D) [1, {yz}]')
case [123, 1 | 2 | 3, 789]:
# 複数要素
print('(E) [123, 1 | 2 | 3, 789]')
case [123, 4 | 5 | 6 as y, 789]:
# 要素キャプチャ
print(f'(F) [123, {y} (4 | 5 | 6), 789]')
case [x, y, z] if x < y < z:
# ガード条件
print(f'(G) [{x}, {y}, {z}] (x < y < z)')
case _:
print('その他')
lst = [123, 456, 789]
match_sequence(lst) # 出力:(A) [123, 456, 789] [123, 456, 789]
tpl1 = (123, 456, 789)
match_sequence(tpl1) # 出力:(A) (123, 456, 789) [123, 456, 789]
tpl2 = 123, 456, 789
match_sequence(tpl2) # 出力:(A) (123, 456, 789) [123, 456, 789]
match_sequence([1, 456, 789]) # 出力:(D) [1, [456, 789]]
match_sequence([123, 2, 789]) # 出力:(E) [123, 1 | 2 | 3, 789]
match_sequence([123, 5, 789]) # 出力:(F) [123, 5 (4 | 5 | 6), 789]
match_sequence([12, 34, 56]) # 出力:(G) [12, 34, 56] (x < y < z)
match_sequence([12, 345, 67]) # 出力:その他
マッピング パターン
def match_mapping(subject):
match subject:
case {'key1': 'V1', 'key2': 'V2'}:
print('(A)')
case {'key1': 'V01', 'key2': 'V02'} | {'key1': 'V11', 'key2': 'V12'}:
print('(B)')
case {'key1': 'V21', 'key2': 'V22'} | {'key1': 'V31', 'key2': 'V32'} as dic:
print(f'(C) {dic}')
case {'key1': v1, 'key2': v2} if int(v1) < int(v2):
print(f'(D) {v1} {v2}')
#case {'key1': 'V1', 'key1': 'V01'}: # SyntaxError (同一キー)
# pass
#case {'keyA': 'VA', 'keyB': 'VB', **_}: # SyntaxError (**_)
# pass
case {'keyA': 'VA', 'keyB': 'VB', **dic} as full:
print(f'(E) {dic}')
print(f' {full}')
case {'keyX': vx, 'keyY': vy} as full if vx < vy:
print(f'(X) {full} (vx < vy)')
case _:
print('(Z) その他')
match_mapping({'key1': 'V1', 'key2': 'V2'}) # 出力:(A)
match_mapping({'key1': 'V1', 'key2': 'V2', 'key3': 'V3'}) # 出力:(A)
match_mapping({'key1': 'V01', 'key2': 'V02'}) # 出力:(B)
match_mapping({'key1': 'V11', 'key2': 'V12'}) # 出力:(B)
match_mapping({'key1': 'V21', 'key2': 'V22'})
# 出力:(C) {'key1': 'V21', 'key2': 'V22'}
match_mapping({'key1': 'V31', 'key2': 'V32'})
# 出力:(C) {'key1': 'V31', 'key2': 'V32'}
match_mapping({'key1': '123', 'key2': '456'})
# 出力:(D) 123 456
match_mapping({'keyA': 'VA', 'keyB': 'VB'})
# 出力:(E) {}
# 出力: {'keyA': 'VA', 'keyB': 'VB'}
match_mapping({'keyA': 'VA', 'keyB': 'VB', 'keyC': 'VC', 'keyD': 'VD'})
# 出力:(E) {'keyC': 'VC', 'keyD': 'VD'}
# 出力: {'keyA': 'VA', 'keyB': 'VB', 'keyC': 'VC', 'keyD': 'VD'}
match_mapping({'keyX': 123, 'keyY': 456})
# 出力:(X) {'keyX': 123, 'keyY': 456} (vx < vy)
match_mapping({'keyX': 123, 'keyY': 45})
# 出力:(Z) その他
match_mapping({'key9': 'V9'}) # 出力:(Z) その他
クラス パターン
class MyClass:
def __init__(self, name):
self._name = name
class MyClass2:
__match_args__ = ('_name',)
def __init__(self, name):
self._name = name
def match_class(subject):
match subject:
case int(100):
print(f'(int-A) [100]')
case int(200 | 300) as n:
print(f'(int-B) {n} [200 | 300]')
case int(n) if n < 0:
print(f'(int-C) {n} (n < 0)')
case int(n):
print(f'(int-D) {n}')
case float(f):
print(f'(float) {f}')
case str(s) if len(s) < 5:
print(f'(str-A) {s} (len(s) < 5)')
case str(s):
print(f'(str-B) {s}')
case MyClass(_name='name'):
print('(MyClass-A) [name]')
case MyClass(_name='NAME'):
print('(MyClass-B) [NAME]')
case MyClass2('name'):
print('(MyClass2-A) [name]')
case MyClass2('NAME'):
print('(MyClass2-B) [NAME]')
case MyClass2(x):
print(f'(MyClass2-C) {x}')
case {"text": str(msg), "color": str(c)}:
print(f'(dict-A) {msg=} {c=}')
case {"text": str(msg), "color": int(c)} if c < 0x80:
print(f'(dict-B) {msg=} {c=} (c < 0x80)')
case {"text": str(msg), "color": int(c)}:
print(f'(dict-C) {msg=} {c=}')
case {"text": MyClass2('name'), "color": str(c)}:
print(f'(dict-D) {c=} [name]')
case {"text": MyClass2('NAME'), "color": str(c)}:
print(f'(dict-E) {c=} [NAME]')
case {"text": MyClass2(name), "color": str(c)}:
print(f'(dict-F) {name=} {c=}')
case _:
print('(Z) その他')
match_class(100) # 出力:(int-A) [100]
match_class(200) # 出力:(int-B) 200 [200 | 300]
match_class(300) # 出力:(int-B) 300 [200 | 300]
match_class(-123) # 出力:(int-C) -123 (n < 0)
match_class(123) # 出力:(int-D) 123
match_class(float(123)) # 出力:(float) 123.0
match_class('str') # 出力:(str-A) str (len(s) < 5)
match_class('string') # 出力:(str-B) string
match_class(MyClass('name')) # 出力:(MyClass-A) [name]
match_class(MyClass('NAME')) # 出力:(MyClass-B) [NAME]
match_class(MyClass('name_x')) # 出力:(Z) その他
match_class(MyClass2('name')) # 出力:(MyClass2-A) [name]
match_class(MyClass2('NAME')) # 出力:(MyClass2-B) [NAME]
match_class(MyClass2('name_x')) # 出力:(MyClass2-C) name_x
match_class({'text': 'MSG', 'color': 'RED'})
# 出力:(dict-A) msg='MSG' c='RED'
match_class({'text': 'MSG', 'color': 0})
# 出力:(dict-B) msg='MSG' c=0 (c < 0x80)
match_class({'text': 'MSG', 'color': 0xFF})
# 出力:(dict-C) msg='MSG' c=255
match_class({'text': MyClass2('name'), 'color': 'RED'})
# 出力:(dict-D) c='RED' [name]
match_class({'text': MyClass2('NAME'), 'color': 'GREEN'})
# 出力:(dict-E) c='GREEN' [NAME]
match_class({'text': MyClass2('name_x'), 'color': 'BLUE'})
# 出力:(dict-F) name='name_x' c='BLUE'
match_class({'text': MyClass2('name'), 'color': 0xFF})
# 出力:(Z) その他