パターンマッチ構文の仕様(PEP-634抄訳)
概要
このPEPは,match構文の技術仕様を提供するものです。これはPEP 622 を置き換えるもので、ここでは3つの部分に分かれています。 このPEPには意図的に解説を入れていません。動機付けと私たちの設計上の選択についてのすべての説明はPEP 635にあります。初めて読む方は、パターンの概念、構文、意味論をやさしく紹介しているPEP 636から始めることをお勧めします。
構文とセマンティクス
完全な文法については、「付録-A」を参照してください。
概要と専門用語
パターン・マッチング・プロセスは、パターン(caseに続く)とサブジェクトの値(matchに続く)を入力として受け取ります。このプロセスを表すフレーズには、「パターンがサブジェクトの値とマッチします」や「パターンをサブジェクトの値とマッチさせます」などがあります。
パターンマッチングの主な結果は、成功か失敗かです。成功した場合は、「パターンが成功した」、「マッチが成功した」、「パターンが対象の値と一致した」などと言います。
多くの場合、パターンにはサブパターンが含まれており、それらのサブパターンと値とのマッチング(or_patternなど)や、値の一部とのマッチング(sequence_patternなど)の成否によって、パターンの成否が決まります。このプロセスは通常、全体の結果が決まるまで、サブパターンを左から右へと処理していきます。たとえば、or_patternは最初に成功したサブパターンで成功し、sequence_pattern は最初に失敗したサブパターンで失敗します。
パターンマッチングの副次的な結果として、1つまたは複数の名前の結合があります。パターンが値を名前にバインドする」と言うこともあります。最初の成功までサブパターンを試した場合、成功したサブパターンによる結合だけが有効となり、最初の失敗まで試した場合、結合はマージされます。これらのケースには、以下に説明するいくつかのルールも適用されます。
パターンマッチ構文
パターンマッチング構文は match と case というふたつのキーワードを使用します。
code: EBNF
match_stmt: "match" subject_expr ':' NEWLINE INDENT case_block+ DEDENT
subject_expr:
| star_named_expression ',' star_named_expressions?
| named_expression
case_block: "case" patterns guard ':' block guard: 'if' named_expression
ルールにある star_named_expression、named_expression および block は Python の標準文法の一部です。
ルールのパターンを以下に示します。
文脈としては,match_stmt は compound_statement の新しい代替手段です.
code: EBNF
patterns: open_sequence_pattern | pattern
pattern: as_pattern | or_pattern
as_pattern: or_pattern 'as' capture_pattern
or_pattern: '|'.closed_pattern+
closed_pattern:
| literal_pattern
| capture_pattern
| wildcard_pattern
| value_pattern
| group_pattern
| sequence_pattern
| mapping_pattern
| class_pattern
as_pattern
code: EBNF
as_pattern: or_pattern 'as' capture_pattern
ASパターンは、asキーワードの左にあるORパターンをサブジェクトに対してマッチさせます。これに失敗した場合、ASパターンは失敗します。そうでなければ、ASパターンはサブジェクトを as キーワードの右にある名前にバインドし、成功します。
or_pattern
code: EBNF
or_pattern: '|'.closed_pattern+
2つ以上のパターンがパイプ記号(|)で区切られているものを ORパターン と呼びます。
1つの閉じたパターンはそのままです。
最後のサブパターンだけが反論(irrefutable)できないことがあります。
各サブパターンは、同じ名前のセットをバインドしなければなりません。
ORパターンは、サブパターンのそれぞれを順番に主語にマッチさせ、一つが成功するまで続けます。そして、そのORパターンは成功したとみなされます。いずれのサブパターンも成功しなかった場合、ORパターンは失敗します。
literal_pattern
code: EBNF
literal_pattern:
| signed_number
| signed_number '+' NUMBER
| signed_number '-' NUMBER
| strings
| 'None'
| 'True'
| 'False'
signed_number: NUMBER | '-' NUMBER
ルール文字列とトークンNUMBERはPythonの標準文法で定義されています。
三重引用符("""...""")の文字列がサポートされています。生の文字列とバイト文字列がサポートされています。F-文字列はサポートされていません。
signed_number '+' NUMBERとsigned_number '-' NUMBERの形式は複素数を表す場合にのみ許されます。これらは左に実数、右に虚数を必要とします。
リテラル・パターンは、対象となる値がリテラルで表現された値と比較して等しければ成功しますが、これには以下の比較ルールが用いられます。
数値と文字列は == 演算子で比較されます。
ングルトン・リテラルの None、True、False は is 演算子で比較されます。
capture_pattern
code: EBNF
capture_pattern: !"_" NAME
単一のアンダースコア(_)は、キャプチャパターンではありません(これは、!"_" が表現しています)。これはwildcard_pattern として扱われます。
capture_pattern は常に成功します。PEP-572 の セイウチ演算子(:=)で確立された名前の結合のためのスコープ・ルールを使用して、対象の値を名前に結合します。(概要: 名前は、適用可能な非ローカルまたはグローバルなステートメントがない限り、最も近い含有関数スコープ内のローカル変数になります。) 与えられたパターンでは、与えられた名前は一度だけ束縛することができます。これにより、例えば case x, x: ... は許されませんが、case [x] | x: .... は許されます。
wildcard_pattern
code: EBNF
wildcard_pattern: "_"
ワイルドカードパターンは常に成功します。名前をバインドすることはありません。
value_pattern
code: EBNF
value_pattern: attr
attr: name_or_attr '.' NAME
name_or_attr: attr | NAME
パターンの中のドットで囲まれた名前は、Pythonの標準的な名前解決ルールを使って検索されます。しかし、同じ値のパターンが同じmatch文の中で複数回出てきた場合、インタプリタは同じ検索を繰り返すのではなく、最初に見つかった値をキャッシュして再利用することがあります。(明確にするために、このキャッシュは、特定の match 文の特定の実行に厳密に結びついています)。
このようにして見つかった値が(== 演算子を使って)対象となる値と比較して等しい場合、パターンは成功します。
group_pattern
code: SYNTAX
group_pattern: '(' pattern ')'
丸括弧付きパターンには追加の構文はありません。丸括弧付きパターンでは、意図したグループ化を強調するためにパターンを括弧で囲むことができます。
sequence_pattern
code: EBNF
sequence_pattern:
maybe_sequence_pattern: ','.maybe_star_pattern+ ','?
maybe_star_pattern: star_pattern | pattern
star_pattern: '*' (capture_pattern | wildcard_pattern)
(ただし、丸括弧で囲まれたパターンで末尾にコンマがないものは、sequence_patternではなく、group_pattern です。しかし、角括弧([...])で囲まれた単一のパターンはsequence_patternのままです)。
...,を使用したsequence_pattern と、(...),を使用したsequence_pattern および open_sequence_patternの間には、意味上の違いはありません。
sequence_patternは、最大で1つのstar_patternを含むことができます。star_patternはどの位置にあってもかまいません。star_patternが存在しない場合、そのsequence_patternは固定長sequence_patternとなり、そうでない場合は可変長sequence_patternとなります。
sequence_patternが成功するためには、subejct が ssequence でなければなりません。ssequenceであるとは、そのクラスが以下のいずれかであると定義されます。
collections.abc.Sequenceを継承したクラス
collections.abc.Sequenceとして登録されているPythonクラス
Py_TPFLAGS_SEQUENCE ビットが設定されている組み込みクラス
上記のいずれかを継承したクラス(親のシーケンス登録前に定義されたクラスを含む
以下の標準ライブラリクラスは、Py_TPFLAGS_SEQUENCEビットが設定されます。
array.array
collections.deque
list
memoryview
range
tuple
注意
str、bytes、bytearray は通常 ssequence とみなされますが、上記のリストには含まれておらず、sequence_pattern にはマッチしません。
固定長sequence_patternは、対象となるシーケンスの長さがサブパターンの数と等しくない場合、失敗する。
可変長sequence_patternは、対象となるシーケンスの長さが、star_patternではないサブパターンの数よりも小さい場合に失敗します。
対象となるシーケンスの長さは、組み込みの len() 関数を使って (つまり、 __len__ プロトコルを使って) 取得します。しかし、インタープリタは、値のパターンについて説明したのと同様の方法で、この値をキャッシュすることができます。
固定長のシーケンスパターンは、サブパターンを対象シーケンスの対応するアイテムに左から右へとマッチングします。マッチングは,サブパターンが失敗するとすぐに停止する(失敗を伴う).すべてのサブパターンが対応するアイテムとのマッチングに成功した場合、シーケンスパターンは成功します。
可変長のシーケンスパターンでは、固定長のシーケンスと同様に、まず先頭の非スター型サブパターンを対象シーケンスの対応するアイテムにマッチさせる。これが成功すると、スター型サブパターンは、残りの主題項目からなるリストにマッチし、スター型サブパターンに続く非スター型サブパターンに対応する項目が最後から削除されます。残りの非星型サブパターンは、固定長のシーケンスの場合と同様に、対応する主題アイテムにマッチします。
mapping_pattern
code: EBNF
items_pattern: ','.key_value_pattern+ ','?
key_value_pattern:
| (literal_pattern | value_pattern) ':' pattern
| double_star_pattern
double_star_pattern: '**' capture_pattern
ただし、**_ はこの構文では許されません。
mapping_pattern には、最大で1つの double_star_pattern を含めることができ、それは最後でなければなりません。
mapping_pattern は、重複するキー値を含んではいけません。(すべてのkey_value_pattern がliteral_patternである場合、これは構文エラーとみなされます。そうでない場合はランタイムエラーとなり、ValueErrorが発生します。)
mapping_pattern が成功するためには、対象がマッピングでなければなりません。マッピングであるということは、そのクラスが以下のいずれかであると定義されます。
collections.abc.Mappingを継承したクラス。
collections.abc.Mappingとして登録されているPythonクラス
Py_TPFLAGS_MAPPING ビットが設定されている組み込みクラス
上記のいずれかを継承したクラス(親のマッピング登録前に定義されたクラスを含む
標準ライブラリクラスのdictおよびmappingproxyには,Py_TPFLAGS_MAPPINGビットが設定されます。
mapping_pattern で指定されたすべてのキーがサブジェクトマッピングに存在し、各キーのパターンがサブジェクトマッピングの対応する項目と一致する場合、マッピングパターンは成功します。キーは常に == 演算子で比較されます。NAME フォームに '**' がある場合、その名前は、サブジェクトのマッピングからの残りのキーと値のペアを含む dict にバインドされます。
mapping_pattern で重複するキーが検出された場合、パターンは無効とみなされ、ValueError が発生します。
キーと値のペアのマッチングは、サブジェクトの get() メソッドの 2 つの引数を使って行われます。そのため、マッチしたキーと値のペアは、マッピングの中にすでに存在していなければならず、__missing__ や __getitem__ によってその場で作成されたものではありません。例えば、collections.defaultdict インスタンスは、match 文が入力された時点で既に存在していたキーを持つパターンにのみマッチします。
class_pattern
code: EBNF
class_pattern:
pattern_arguments:
| positional_patterns keyword_patterns
| keyword_patterns
positional_patterns: ','.pattern+
keyword_patterns: ','.keyword_pattern+
keyword_pattern: NAME '=' pattern
class_pattern では、同じキーワードを複数回繰り返すことはできません。
name_or_attr が内蔵型のインスタンスでない場合、TypeError が発生します。
主体が name_or_attr のインスタンスでない場合、class_pattern は失敗します。これは isinstance() を使ってテストされます。
引数が存在しない場合、isinstance() のチェックが成功すればパターンは成功します。それ以外の場合は
keyword_patternsのみが存在する場合、それらは以下のように1つずつ処理されます。
キーワードはサブジェクトの属性として検索されます。
この処理で AttributeError 以外の例外が発生した場合は、その例外が伝搬されて現れます。
AttributeErrorが発生した場合、class_pattern は失敗します。
それ以外の場合は、キーワードに関連するサブパターンが属性値と照合されます。これが失敗すると、class_pattern は失敗します。これが成功すると、マッチングは次のキーワードに進みます。
すべてのkeyword_patterns が成功すれば、class_pattern 全体が成功したことになります。
位置指定のパターンが存在する場合、それらは keyword_patterns に変換され(以下を参照)、追加のkeyword_patterns として扱われ、(もしあれば)構文上のkeyword_patternsの前に置かれます。
positional_patterns は、name_or_attr で指定されたクラスの __match_args__ 属性を使って、以下のようにキーワード・パターンに変換されます。
いくつかの組み込み型(以下に規定)では、サブジェクト全体にマッチする単一の位置サブパターンが受け入れられます。(keyword_patterns はここでは他の型と同様に機能します。)
getattr(cls, "__match_args__", ()) と同等のものが呼ばれます。
これが例外を発生させる場合は、例外が伝搬されます。
返された値がタプルでない場合、変換は失敗し、TypeErrorが発生します。
もし位置指定パターンの数が __match_args__ の長さ(len() を使って得られる長さ)よりも多い場合、TypeError が発生します。
そうでなければ、positional_patterns i は __match_args__[i] をキーワードとしたキーワードパターンに変換されますが、後者は文字列であることが条件です。
また、キーワードが重複している場合には TypeError が発生します。
positional_patterns が keyword_patterns に変換されると,keyword_patterns しかない場合と同様にマッチが行われます.
上述したように,以下の組み込み型では,位置サブパターンの扱いが異なります: bool,bytearray,bytes,dict,float,frozenset,int,list,set,str,tuple。
この動作は,おおよそ次のようになります。
code: python
class C:
__match_args__ = ("__match_self_prop__",)
@property
def __match_self_prop__(self):
return self
副作用と未定義の動作
マッチング処理で明示的に発生する唯一の副作用は、名前の結合です。しかし、このプロセスでは、サブジェクトとそのコンポーネントの一部について、属性アクセス、インスタンス・チェック、len()、等式、およびアイテム・アクセスに依存しています。また、値パターンやクラスパターンのクラス名も評価します。これらは通常、副作用を引き起こすことはありませんが、理論的には可能性があります。この提案では、どのメソッドが何回呼び出されるかについての仕様を意図的に除外しています。したがって、この動作は未定義であり、ユーザーコードはこれに依存してはいけません。
もう1つの未定義の動作は、キャプチャパターンによる変数の結合で、その後(同じケースブロック内で)別のパターンが失敗することです。唯一の制約は、キャプチャ変数を明示的に使用するガードが評価される前に、キャプチャ変数が設定されていなければならないということです。ガードがand節で構成されている場合、左から右への評価順序が維持されている限り、オペランドの評価がパターンマッチの間に挟まれても構いません。
標準ライブラリ
パターン・マッチングの使用を容易にするために、標準ライブラリにいくつかの変更が加えられています。
名前付きタプルとデータクラスは、自動的に生成された__match_args__を持ちます。
データクラスの場合、生成される__match_args__の属性の順序は、生成される__init__()メソッドの対応する引数の順序と同じになります。これは、属性がスーパークラスから継承されている場合も含みます。init=False のフィールドは__match_args__から除外されます。
さらに、既存の標準ライブラリ・クラスを調べて、有益と思われるところに__match_args__を追加することに、系統的な努力が払われます。
付録A -- 完全な文法
match_stmtの完全な文法を示します。これはcompound_stmtの追加の選択肢です。match と case はソフトキーワードであり、他の文法上の文脈では予約語ではないことを覚えておいてください(コロンがない場合の行頭を含む)。慣習上、ハードキーワードは一重引用符、ソフトキーワードは二重引用符を使用します。
標準的なEBNF以外に使用されている記法。
SEP.RULE+ は、RULE(SEP RULE)* の略記です。
!RULE はRULEの否定
code: EBNF
match_stmt: "match" subject_expr ':' NEWLINE INDENT case_block+ DEDENT
subject_expr:
| named_expression
case_block: "case" patterns guard ':' block guard: 'if' named_expression
patterns: open_sequence_pattern | pattern
pattern: as_pattern | or_pattern
as_pattern: or_pattern 'as' capture_pattern
or_pattern: '|'.closed_pattern+
closed_pattern:
| literal_pattern
| capture_pattern
| wildcard_pattern
| value_pattern
| group_pattern
| sequence_pattern
| mapping_pattern
| class_pattern
literal_pattern:
| signed_number !('+' | '-')
| signed_number '+' NUMBER
| signed_number '-' NUMBER
| strings
| 'None'
| 'True'
| 'False'
signed_number: NUMBER | '-' NUMBER
capture_pattern: !"_" NAME !('.' | '(' | '=')
wildcard_pattern: "_"
value_pattern: attr !('.' | '(' | '=')
attr: name_or_attr '.' NAME
name_or_attr: attr | NAME
group_pattern: '(' pattern ')'
sequence_pattern:
maybe_sequence_pattern: ','.maybe_star_pattern+ ','?
maybe_star_pattern: star_pattern | pattern
star_pattern: '*' (capture_pattern | wildcard_pattern)
items_pattern: ','.key_value_pattern+ ','?
key_value_pattern:
| (literal_pattern | value_pattern) ':' pattern
| double_star_pattern
double_star_pattern: '**' capture_pattern
class_pattern:
pattern_arguments:
| positional_patterns keyword_patterns
| keyword_patterns
positional_patterns: ','.pattern+
keyword_patterns: ','.keyword_pattern+
keyword_pattern: NAME '=' pattern