#37. パターンマッチング

パターン マッチングは Python の構造マッチング システムです。それを実装するのは、match声明とcase条項。```python match value: case 0: result = "zero" case [x, y]: result = x + y case {"name": name}: result = name case _: result = None


CPython レベルでは、パターン マッチングは通常のバイトコードと特殊なマッチング命令にコンパイルされます。インタプリタはサブジェクトを評価し、各ケースを順番に試し、一致が成功した場合に名前をバインドし、選択された本文にジャンプします。

## 37.1`match`声明

`match`ステートメントには 1 つの主語式と 1 つ以上のケースがあります。```python
match subject:
    case pattern:
        body
    case pattern if guard:
        body
    case _:
        body
```サブジェクト式は 1 回評価されます。```python
match compute():
    case 1:
        ...
    case 2:
        ...

compute()は 1 回実行されます。次に、結果がケースに対して上から下までテストされます。

概念的には:```text subject = compute()

try case 1 if match succeeds: execute case 1 body else: try case 2


## 37.2 パターンと式

パターンは式に似ていますが、異なるルールに従います。```python
match value:
    case x:
        ...
```これは比較にならない`value`既存の変数を使用する`x`。主題を新しい名前に取り込みます`x`

キャプチャ パターンは常に成功します。```python
case x:
```手段:```text
bind x = subject
match succeeds
```名前付き定数と比較するには、ドット区切りの名前を使用します。```python
case Color.RED:
```またはガードを使用します。```python
case x if x == expected:
```パターン マッチングには独自の構文とバインディング ルールがあるため、この区別は重要です。

## 37.3 ケースの順序

事件は順番に審理されます。```python
match value:
    case int():
        result = "integer"
    case bool():
        result = "boolean"
```これは間違った順序です。`bool`のサブクラスです`int````python
isinstance(True, int)
```それは本当です。

それで`True`マッチ`int()`到達する前に`bool()`

より良い:```python
match value:
    case bool():
        result = "boolean"
    case int():
        result = "integer"
```パターン マッチングでは、ランタイムの型と構造が使用されます。通常、より具体的なケースは、より一般的なケースの前に登場する必要があります。

## 37.4 ワイルドカードパターン

ワイルドカードパターン`_`何でも一致し、何もバインドしません。```python
match value:
    case 0:
        result = "zero"
    case _:
        result = "other"
````_`パターンは通常、最終フォールバックとして使用されます。

通常のキャプチャ名とは異なります。```python
case name:
```これはバインドします`name````python
case _:
```これは拘束しない`_`試合結果の新しいローカルとして。

## 37.5 リテラルパターン

リテラル パターンは、数値、文字列、バイト、ブール値、および`None````python
match value:
    case 0:
        result = "zero"
    case "ok":
        result = "success"
    case None:
        result = "missing"
```ほとんどのリテラルの場合、一致には等価セマンティクスが使用されます。```text
subject == literal
```次のようなシングルトン定数の場合`None``True` そして`False`、マッチングはアイデンティティ スタイルのシングルトン セマンティクスを使用します。

リテラル パターンは、タグ、小さな状態、プロトコル マーカーに役立ちます。

## 37.6 パターンのキャプチャ

キャプチャ パターンは名前をバインドします。```python
match value:
    case x:
        result = x
```これは常に一致します。

キャプチャ パターンは、より大きなパターン内で役立ちます。```python
match point:
    case [x, y]:
        result = x + y
```ここ、`[x, y]`シーケンスパターンです。名前`x`そして`y`要素をキャプチャします。

一致が成功すると、バインドされた名前がケース本文で使用できるようになります。```python
match value:
    case [x, y]:
        print(x, y)
```一致が失敗した場合、その失敗したパターンからのバインディングが、ユーザー コードが依存できる形で漏洩してはなりません。

## 37.7 値のパターン

値パターンは、サブジェクトをドット付きの名前で参照される値と比較します。```python
match color:
    case Color.RED:
        handle_red()
    case Color.BLUE:
        handle_blue()
```ドット付きの名前が評価され、件名と比較されます。

これは次のものとは異なります。```python
case RED:
```それは、と呼ばれる名前に取り込まれます`RED`

定数の場合は、ドット付きの名前、列挙型、またはガードを使用します。```python
class Status:
    OK = "ok"
    ERROR = "error"

match status:
    case Status.OK:
        ...
```## 37.8 OR パターン

OR パターンは、いずれかの代替が一致する場合に一致します。```python
match value:
    case 0 | 1 | 2:
        result = "small"
```すべての代替は、同じ名前のセットをバインドする必要があります。

有効:```python
match value:
    case [x] | (x,):
        result = x
```原則として無効:```python
case [x] | [x, y]:
```最初の選択肢がバインドするため`x`2番目のバインド中`x`そして`y`

コンパイラは、OR パターンの名前バインディングの一貫性をチェックします。

## 37.9 ASパターン

AS パターンは、サブパターンとも照合しながら、一致した値全体をバインドします。```python
match value:
    case [x, y] as pair:
        print(x, y, pair)
```件名が一致していれば`[x, y]` それから:```text
x = first element
y = second element
pair = whole subject
```これは、構造化されていないフィールドと元の値の両方が必要な場合に便利です。

## 37.10 ガード

警備員というのは、`if`ケース付属の状態。```python
match value:
    case [x, y] if x < y:
        result = "ascending"
```最初にパターンが一致します。成功すると、ガードが評価されます。

概念的には:```text
if subject matches [x, y]:
    if x < y:
        execute body
    else:
        try next case
```ガードは任意の Python コードを実行できます。例外を発生させたり、関数を呼び出したり、状態を変更したり、キャプチャされた名前に依存したりすることができます。

ガードが false と評価された場合、そのケースは選択されていないものとして扱われ、次のケースで照合が続行されます。

## 37.11 シーケンスパターン

シーケンス パターンは、シーケンスのようなオブジェクトと一致します。```python
match value:
    case [x, y]:
        result = x + y
```これは長さ 2 のシーケンスに一致します。

一致する可能性のある例:```python
[1, 2]
(1, 2)
```文字列とバイトは、他のコンテキストではシーケンスのように扱われますが、この目的ではシーケンスとして扱われません。

シーケンスマッチングでは以下をチェックする必要があります。```text
is the subject a sequence pattern candidate
does it have the required length
extract elements
match nested subpatterns
bind names
```## 37.12 スター付きシーケンスパターン

星付きパターンは、可変長の中間セクションをキャプチャします。```python
match value:
    case [first, *middle, last]:
        ...
```のために:```python
value = [1, 2, 3, 4]
```バインディングは次のとおりです。```text
first = 1
middle = [2, 3]
last = 4
```スター付きキャプチャはリストを受け取ります。

このパターンには、星印のない位置に少なくとも十分な要素が必要です。```python
case [first, *middle, last]:
```少なくとも 2 つの要素が必要です。

## 37.13 マッピングパターン

マッピング パターンは、マッピングに似たオブジェクトと一致します。```python
match value:
    case {"name": name, "age": age}:
        ...
```これにより、サブジェクトが必要なキーを持っているかどうかがチェックされます。

のために:```python
value = {"name": "Ada", "age": 37, "city": "London"}
```パターンが一致してバインドされます。```text
name = "Ada"
age = 37
```パターンがパターン外のより厳密なロジック (ガードなど) を使用しない限り、追加のキーが許可されます。

マッピング パターンでは、残りの項目もキャプチャできます。```python
match value:
    case {"name": name, **rest}:
        ...
```それから`rest`一致しないキーの辞書を受け取ります。

## 37.14 クラスパターン

クラス パターンは、タイプと属性によってオブジェクトを照合します。```python
match value:
    case Point(x, y):
        ...
```これにより、`value`の例です`Point`、フィールドを抽出します。

位置フィールドの意味は次によって制御されます。`Point.__match_args__`

例:```python
class Point:
    __match_args__ = ("x", "y")

    def __init__(self, x, y):
        self.x = x
        self.y = y
```それから:```python
match p:
    case Point(x, y):
        ...
```概念的には次のものに似ています。```text
isinstance(p, Point)
x = p.x
y = p.y
```パターンマッチングセマンティクスと失敗動作を備えています。

## 37.15 キーワードクラスパターン

クラス パターンでは、属性に明示的に名前を付けることができます。```python
match value:
    case Point(x=0, y=y):
        ...
```これは、`Point`だれの`x`属性の一致`0`をバインドします。`y`属性。

キーワード クラス パターンは依存しません`__match_args__`それらの属性のために。

概念的には:```text
check isinstance(value, Point)
get value.x and match against 0
get value.y and bind y
```属性検索ではコードを実行できるため、プロパティまたはカスタム属性へのアクセスが関係する場合、クラス パターンに副作用が生じる可能性があります。

## 37.16`__match_args__`

`__match_args__`位置クラス パターン フィールドを属性名にマップします。```python
class Point:
    __match_args__ = ("x", "y")
```これにより、次のことが可能になります。```python
case Point(a, b):
```意味:```python
case Point(x=a, y=b):
```データクラスと名前付きタプルに関して、Python は多くの場合、便利な機能を提供します。`__match_args__`自動的に。

もし`__match_args__`が欠落している、空である、または互換性がない場合、位置クラス パターンはパターンの形式に応じて失敗するかエラーが発生する可能性があります。

## 37.17 入れ子になったパターン

パターンはネストできます。```python
match value:
    case {"point": Point(x, y), "label": label}:
        ...
```これにより以下がチェックされます。```text
subject is a mapping
subject has key "point"
subject has key "label"
subject["point"] is a Point
extract Point.x and Point.y
bind label
```ネストされたパターンは、一連のテストと抽出操作にコンパイルされます。

ネストされた部分のいずれかが失敗すると、ケース全体が失敗し、照合は次のケースで続行されます。

## 37.18 名前のバインド

成功した場合にバインドされた名前は、周囲のスコープのローカル変数になります。```python
def f(value):
    match value:
        case [x, y]:
            return x + y
        case _:
            return 0
```コンパイラが扱うのは、`x`そして`y`地元の名前として`f`

これはスコープ分析に影響を与える可能性があります。パターンによってキャプチャされた名前は、割り当てと同様に、バインディングの発生です。

例:```python
def f(value):
    match value:
        case x:
            return x
```ここ、`x`の地元です`f`

## 37.19 失敗したバインディングの一致

失敗したパターンは、安定したバインディングのソースとして使用しないでください。

例:```python
match value:
    case [x, 0]:
        ...
    case _:
        ...
```構造体の一部をバインドした後で最初のケースが失敗した場合、CPython は部分的なバインディングを成功結果として公開することを回避する必要があります。

言語セマンティクスにより、一致が失敗したときにバインドされた名前に依存することはできません。

コンパイラとインタープリタのコードはこのルールを維持する必要があります。

## 37.20 バイトコードのパターンマッチング

パターン マッチングはバイトコードにコンパイルされます。

簡単な例:```python
def f(value):
    match value:
        case [x, y]:
            return x + y
        case _:
            return 0
```概念的なバイトコード構造:```text
load subject

try sequence case:
    check sequence
    check length 2
    unpack into x, y
    execute body
    return

fallback:
    return 0
```CPython には、シーケンス マッチング、マッピング マッチング、キー抽出、クラス マッチングなどの一般的なチェックのためのパターン固有のオペコードが含まれています。正確なオペコード名とレイアウトはバージョン間で異なる場合があります。

## 37.21 主題の重複

多くの場合、複数のケースを試している間、被験者は利用可能な状態を維持する必要があります。```python
match value:
    case [x]:
        ...
    case {"x": x}:
        ...
    case _:
        ...
```最初のケースが失敗した場合でも、2 番目のケースには元の件名が必要になります。

コンパイラは、必要に応じてサブジェクトを複製、保存、または破棄するためのスタック操作を発行します。

概念的には:```text
subject on stack
try case 1
    if fail, restore subject
try case 2
    if fail, restore subject
fallback
```パターンマッチングには多くの失敗パスがあるため、正しいスタック規律が重要です。

## 37.22 障害パス

ケースは多くの点で失敗する可能性があります。```text
wrong type
wrong sequence length
missing mapping key
class check fails
attribute extraction fails with AttributeError
nested subpattern fails
OR alternative fails
guard is false
```一部の失敗は通常の不一致です。その他は真の例外です。

パターン マッチングでは以下を区別する必要があります。```text
normal pattern failure
real runtime exception
```たとえば、マッピング キーが欠落している場合は、マッピング パターンが一致しないことを意味します。

しかし、プロパティゲッターは、`RuntimeError`クラスパターン中に例外を伝播する必要があります。

## 37.23 ガードと例外

ガードは通常の Python コードです。```python
match value:
    case x if check(x):
        ...
```もし`check(x)`発生すると、例外が伝播します。インタプリタは、これを失敗した一致として扱いません。

概念的には:```text
pattern succeeds
evaluate guard
    returns true: select case
    returns false: try next case
    raises: propagate exception
```これにより、ガードは強力になりますが、効果的でもあります。

## 37.24 マッピングキーの検索

マッピング パターンではキー検索を使用します。次のようなパターンです。```python
case {"name": name}:
```件名にキーが含まれているかどうかを確認します`"name"`そしてその値を抽出します。

実装では、マッピング パターン ルールと比較してセマンティクスを変更する動作を誤って呼び出すことを回避する必要があります。

重要なソースレベルの動作は次のとおりです。```text
required keys must exist
their values must match subpatterns
extra keys are allowed
```もし`**rest`が存在する場合、一致しないキーと値のペアは新しい辞書にコピーされます。

## 37.25 クラスパターン属性の検索

クラス パターンは属性検索を実行できます。```python
case Point(x=x, y=y):
```これにより、以下が呼び出されます。```text
Point-related descriptors
properties
__getattribute__
__getattr__
```属性アクセスが発生した場合`AttributeError`の場合、クラス パターンはその属性に一致しないとして失敗する可能性があります。他の例外も伝播します。

例:```python
class C:
    @property
    def x(self):
        raise RuntimeError("bad")

match C():
    case C(x=x):
        ...
```これが伝播する`RuntimeError`

## 37.26 パターンマッチングと記述子

クラス パターンは属性アクセスを使用するため、記述子も参加できます。```python
class C:
    @property
    def x(self):
        return 10

match C():
    case C(x=10):
        result = "matched"
```プロパティゲッターはマッチング中に実行されます。

これは、パターン マッチングがユーザー コードをトリガーできることを意味します。必ずしも受動的な構造検査ではありません。

## 37.27 パターンマッチングとデータクラス

データクラスはクラス パターンと自然に連携します。```python
from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

def f(p):
    match p:
        case Point(0, y):
            return y
        case Point(x, y):
            return x + y
```データクラスは通常、次のことを提供します。`__match_args__`フィールドに基づいているため、位置クラス パターンが機能します。

概念的には:```text
Point(0, y)
    matches Point instance
    checks x == 0
    binds y from p.y
```## 37.28 パターンマッチングと列挙型

列挙型は値パターンとともによく使用されます。```python
from enum import Enum

class TokenKind(Enum):
    NAME = 1
    NUMBER = 2

match kind:
    case TokenKind.NAME:
        ...
    case TokenKind.NUMBER:
        ...
```ドット付きの名前は値パターンであるため、既存の列挙メンバーと比較されます。

次のことは書かないでください:```python
case NAME:
```新しい変数をキャプチャするつもりがない限り。

## 37.29 パターンマッチングとAST

パターン マッチングは、ツリー状の構造に役立ちます。```python
match node:
    case BinOp(left, "+", right):
        ...
    case Literal(value):
        ...
```コンパイラまたはインタープリタは、クラス パターンを使用してノード タイプを検査できます。

例:```python
@dataclass
class Literal:
    value: object

@dataclass
class BinOp:
    left: object
    op: str
    right: object
```それから:```python
match node:
    case BinOp(Literal(a), "+", Literal(b)):
        return Literal(a + b)
```これは構造分解を直接表現します。

## 37.30 パターンマッチングとバイトコードスタック

パターン マッチングでは、他のバイトコードと同様にフレーム値スタックを使用します。

サブジェクト、中間抽出値、比較結果、および保存されたフォールバック値はスタック上に存在する場合があります。

シーケンスパターン:```python
case [x, y]:
```概念的には次のことを行います。```text
subject
check sequence
check length
unpack two elements
store x
store y
execute body
```失敗すると、一時的な値がポップされ、次のケースはクリーンなスタック形状から開始されます。

## 37.31 コンパイラの責任

コンパイラは、いくつかの不変条件を保持するバイトコードを生成する必要があります。```text
subject evaluated once
cases tried in order
successful pattern bindings visible in body
failed pattern bindings not relied on
guards evaluated after binding
guards can access bound names
stack shape valid at every jump
exceptions propagate correctly
only first successful case runs
```したがって、パターン マッチングは言語機能とコンパイラ機能の両方です。

## 37.32 実行時の責任

実行時に、CPython は以下を実行する必要があります。```text
type checks
sequence checks
mapping checks
class checks
attribute extraction
key extraction
equality checks
binding
guard evaluation
failure jumps
exception propagation
```これらの操作の一部は汎用の Python 操作であり、ユーザー コードを呼び出すことができます。

例えば:```python
case SomeClass(x=x):
```の記述子ロジックを実行する可能性があります`x````python
case 10:
```等価比較を使用することもできます。

パターン マッチングは、Python の動的オブジェクト モデルの下で正しいままでなければなりません。

## 37.33 パターンマッチングと等価性

リテラル パターンと値パターンでは等価性を使用できます。```python
case 10:
```主語が等しい場合に一致します`10`

ユーザー定義オブジェクトの場合、等価性により呼び出し可能`__eq__````python
class C:
    def __eq__(self, other):
        print("compare")
        return True

match C():
    case 10:
        print("matched")
```これにより、ユーザーコードを実行できます。

等価によって例外が発生した場合、例外は伝播します。

## 37.34 パターンマッチングとパフォーマンス

パターン マッチングは明らかですが、魔法ではありません。実行時チェックは引き続き実行されます。

費用には以下が含まれる場合があります。```text
type checks
length checks
mapping lookups
attribute lookups
equality comparisons
temporary allocations
guard execution
bytecode branches
```シンプルな`if`非常に小さいスカラーの場合は、chain の方が高速になる可能性があります。

パターン マッチングは、構造が重要な場合に最も役立ちます。```text
nested data
AST nodes
protocol messages
token streams
configuration shapes
command parsing
event dispatch
```透明度を向上させる場合に使用します。

## 37.35 パターンマッチングと比較`if/elif`これ:```python
match value:
    case 0:
        ...
    case 1:
        ...
```次のようになります。```python
if value == 0:
    ...
elif value == 1:
    ...
```しかし、パターン マッチングはさらに進みます。```python
match value:
    case {"type": "user", "name": name}:
        ...
    case ["move", x, y]:
        ...
    case Point(x=0, y=y):
        ...
```タイプテスト、形状テスト、開梱、バインディングを組み合わせます。

## 37.36 パターンマッチングと代入の構造化

代入を分割するには、形状が一致するかレイズされる必要があります。```python
x, y = value
```もし`value`正確に 2 つの項目がない場合は、それが発生します。

Pattern matching tests shape and can fall through.```python
match value:
    case [x, y]:
        ...
    case _:
        ...
```一致しない`[x, y]`次のケースを試してみるだけです。

したがって、パターン マッチングは条件付きの構造化です。

## 37.37 パターンマッチングとスコープ

パターンでキャプチャされた名前はローカル バインディングです。```python
def f(value):
    match value:
        case x:
            return x
```コンパイラが扱うのは、`x`ローカルとして`f`

これは、外部変数との比較を期待していたユーザーを驚かせる可能性があります。```python
expected = 10

def f(value):
    match value:
        case expected:
            return True
```これはローカル名に取り込まれます`expected`。外装とは比べ物になりません`expected`

使用:```python
def f(value):
    match value:
        case x if x == expected:
            return True
```または、ドット付き定数を使用します。

## 37.38 パターンマッチングと到達不能のケース

キャプチャ パターンはすべてをキャッチします。```python
match value:
    case x:
        ...
    case 0:
        ...
```2 番目のケースは到達不能です。

同様に:```python
case _:
```最終的なケースではないため、その後のケースに到達できなくなります。

コンパイラは、無効な位置でいくつかの反論の余地のないパターンを検出します。

反論の余地のないパターンとは、次のような常に一致するパターンです。```text
_
x
object()
```多くの文脈で。

## 37.39 パターンマッチングと`object()`引数のないクラス パターンは、そのクラスの任意のインスタンスと一致します。```python
case object():
```ほぼすべての通常のオブジェクトは次のインスタンスであるため、`object`, これは大まかに一致します。

以下とは異なります。```python
case object:
```これはキャプチャ パターンです。`object`ただし、構文がコンテキストによって異なる方法で解決される場合を除きます。パターン内ではあいまいな名前を避けてください。

## 37.40 パターンマッチングと`None`合わせるために`None`、 使用:```python
case None:
```これは文字通りのシングルトン パターンです。

以下のものは使用しないでください。```python
case x:
```外側のものと比較することを期待します`x`

シングルトン パターン`None``True` そして`False`一般的で明確です。

## 37.41 パターンマッチングとセキュリティ

パターン マッチングによりユーザー コードが実行される可能性があります。

潜在的な実行ポイントは次のとおりです。```text
equality comparison
attribute access
descriptor access
mapping methods
sequence methods
guard expressions
```信頼できるメモリ内オブジェクトの場合、これは正常です。

敵対的なメソッドを持つ信頼できないオブジェクトの場合、パターン マッチングは安全な宣言クエリとしてではなく、通常の Python 実行のように扱われる必要があります。

## 37.42 パターンマッチングの検査

使用する`dis`一致するバイトコードを検査します。```python
import dis

def f(value):
    match value:
        case [x, y]:
            return x + y
        case {"x": x}:
            return x
        case _:
            return 0

dis.dis(f)
```探す:```text
subject handling
pattern-specific checks
unpack operations
mapping operations
jumps between cases
stores for captured names
guard evaluation
return paths
```正確なバイトコードは Python のバージョンによって異なります。

## 37.43 最小限のパターンマッチャー

リスト用の小さな構造マッチャーがそのアイデアを示すことができます。```python
def match_pair(value):
    if isinstance(value, (list, tuple)) and len(value) == 2:
        x, y = value
        return True, {"x": x, "y": y}
    return False, {}
```使用:```python
ok, binds = match_pair([1, 2])
if ok:
    print(binds["x"] + binds["y"])
```これは次の一部を反映しています。```python
match value:
    case [x, y]:
        print(x + y)
```CPython は、バインディングの辞書ではなく、コンパイルされたバイトコードとランタイム ヘルパーを通じてこれを行います。

## 37.44 よくある誤解

|誤解 |正しいモデル |
|---|---|
|`match`| は単なる switch ステートメントです。構造的なマッチングとバインディングを実行します。
|`case x`変数と比較します`x`|被写体を捉えます`x` |
| `_`は通常のキャプチャです |`_`はパターン内のワイルドカードです |
|サブジェクトはケースごとに評価されます |一度評価されます |
|パターン マッチングには副作用はありません |等価性、記述子、ガード、および属性フックを呼び出すことができます。
|失敗した一致は例外です。通常パターンの失敗は次のケースに移行します。
|クラス パターンはフィールドを直接検査します。彼らは属性アクセスを使用し、`__match_args__`|
|パターン マッチングは常に次よりも高速です`if`|実行時チェックは引き続き実行されます。

## 37.45 読書戦略

スカラーの場合から始めます。```python
def f(x):
    match x:
        case 0:
            return "zero"
        case _:
            return "other"
```次に、構造を追加します。```python
def f(x):
    match x:
        case [a, b]:
            return a + b
        case {"name": name}:
            return name
```次にクラスを追加します。```python
class Point:
    __match_args__ = ("x", "y")

def f(p):
    match p:
        case Point(0, y):
            return y
        case Point(x, y):
            return x + y
```各バージョンについて:```python
import dis
dis.dis(f)
```追跡:```text
where the subject is stored
where each case begins
which checks can fail normally
which operations can raise
where names are bound
where guards run
where fallback jumps go
```## 37.46 章の概要

パターン マッチングは、CPython の構造的な条件付きディスパッチ システムです。サブジェクトを 1 回評価し、ケースを順番に試し、パターンをチェックし、名前をバインドし、ガードを評価し、最初に一致した本体を実行します。

コアモデルは次のとおりです。

```テキスト
件名を 1 回評価する
    
ケースパターンを試す
    
パターンが失敗した場合: クリーンアップして次のケースを試す
    
パターンが成功した場合: バインド名
    
ガードが存在する場合: ガードを評価する
    
ガードが true または存在しない場合: 本体を実行