Pampy は非常に小さく (150 行)、かなり高速で、多くの場合、コードが読みやすくなり、推論が容易になります。 Pampy.js と呼ばれる JavaScript バージョンもあります。
パターンは出現順に評価されます。
演算子 _ は、「思いつかなかったその他のケース」を意味します。
pampy インポート一致から、_def fibonacci(n):return match(n,1, 1,2, 1,_, lambda x: fibonacci(x-1) + fibonacci(x-2) )
from pampy import match, REST, _def lisp(exp):return match(exp,int, lambda x: x,callable, lambda x: x, (呼び出し可能、REST)、ラムダ f、残り: f(*map(lisp,rest))、タプル、ラムダ t: list(map(lisp, t))、 )plus = ラムダ a, b: a + bminus = ラムダ a, b: a - bfrom functools import raiselisp((plus, 1, 2)) # => 3lisp((plus, 1, (minus, 4, 2)) ) # => 3lisp((reduce, plus, (range, 10))) # => 45
match(x,3, "これは数値 3 と一致します",int, "任意の整数と一致します", (str, int), lambda a, b: "関数で使用できるタプル (a, b)", [1, 2, _], "[1, 2] で始まる 3 つの要素の任意のリスト", {'x': _}, "キー 'x' とそれに関連付けられた値を持つ任意の辞書",_, "その他すべて")
from pampy import match, HEAD, TAIL, _x = [1, 2, 3]match(x, [1, TAIL], lambda t: t) # => [2, 3]match(x, [HEAD, TAIL] 、ラムダ h、t: (h, t)) # => (1, [2, 3])
TAIL
とREST
実際には同じ意味です。
pampy インポート match から、_x = [1, [2, 3], 4]match(x, [1, [_, 3], _], lambda a, b: [1, [a, 3], b] ) # => [1, [2, 3], 4]
ペット = { '種類': '犬', '詳細': { '年齢': 3 } }match(pet, { '詳細': { '年齢': _ } }, ラムダ年齢: 年齢) # => 3match (ペット, { _ : { '年齢': _ } }, ラムダ a, b: (a, b)) # => ('詳細', 3)
dict 内に複数の _ を入れると機能しないように感じます。 dict での順序付けは保証されていませんか?ただし、Python 3.7 では、dict がデフォルトで挿入キーの順序を維持するため、これが行われます。
class Pet: パスクラス Dog(Pet): パスクラス Cat(Pet): パスクラス Hamster(Pet): passdef what_is(x):return match(x,Dog, 'dog',Cat, 'cat',Pet, 'その他のペット', _, 'これは決してペットではありません', )what_is(Cat()) # => 'cat'what_is(Dog()) # => 'dog'what_is(Hamster()) # => 'その他のペット'what_is(Pet()) # => '任意other pet'what_is(42) # => 'これはまったくペットではありません'
Pampy は Python 3.7 データクラスをサポートしています。演算子_
引数として渡すと、それらのフィールドと一致します。
@dataclassclass Pet:name: strage: intpet = Pet('rover', 7)match(pet, Pet('rover', _), lambda age: age) # => 7match(pet, Pet(_, 7),ラムダ名: 名前) # => 'ローバー'match(pet, Pet(_, _), ラムダ名, 年齢: (名前, 年齢)) # => ('ローバー', 7)
Pampy は注釈の入力をサポートしています。
クラス Pet: パスクラス Dog(Pet): パスクラス Cat(Pet): パスクラス Hamster(Pet): passtimestamp = NewType("year", Union[int, float])def annotated(a: Tuple[int, float], b: str, c: E) -> タイムスタンプ:passmatch((1, 2), Tuple[int, int], lambda a, b: (a, b)) # => (1, 2)match(1, Union[str, int], lambda x: x) # => 1match('a', Union[str, int], lambda x: x) # => 'a'match('a' , Optional[str], lambda x: x) # => 'a'match(None, Optional[str], lambda x: x) # => Nonematch(Pet, Type[Pet], lambda x: x) # => Petmatch(Cat, Type[Pet], lambda x: x) # => Catmatch(Dog, Any, lambda x: x) # => Dogmatch(Dog, Type[Any], lambda x: x) # => Dogmatch(15, タイムスタンプ, ラムダ x: x) # => 15match(10.0, タイムスタンプ, ラムダ x: x) # => 10.0match([1, 2, 3], List[int], lambda x: x) # => [1, 2, 3]match({'a': 1, 'b': 2}, Dict[str , int], lambda x: x) # => {'a': 1, 'b': 2}match(annotated, Callable[[Tuple[int, float], str, Pet]、タイムスタンプ]、ラムダ x: x) # => 注釈付き
反復可能なジェネリックスの場合、値の実際の型は最初の要素に基づいて推測されます。
match([1, 2, 3], List[int], lambda x: x) # => [1, 2, 3]match([1, "b", "a"], List[int], lambda x: x) # => [1, "b", "a"]match(["a", "b", "c"], List[int], lambda x: x) # MatchErrormatch([" を発生させる) ”、 "b", "c"], List[Union[str, int]], lambda x: x) # ["a", "b", "c"]match({"a": 1, "b" : 2}, Dict[str, int], lambda x: x) # {"a": 1, "b": 2}match({"a": 1, "b": "dog"}, Dict[ストラ、 int], lambda x: x) # {"a": 1, "b": "dog"}match({"a": 1, 1: 2}, Dict[str, int], lambda x: x) # {"a": 1, 1: 2}match({2: 1, 1: 2}, Dict[str, int], lambda x: x) # raises MatchErrormatch({2: 1, 1: 2}, Dict[Union[str, int], int], ラムダ x: x) # {2: 1, 1: 2}
反復可能なジェネリックは、そのサブタイプのいずれとも一致します。
match([1, 2, 3], Iterable[int], lambda x: x) # => [1, 2, 3]match({1, 2, 3}, Iterable[int], lambda x: x) # => {1, 2, 3}match(range(10), Iterable[int], lambda x: x) # => range(10)match([1, 2, 3], List[int], lambda x: x) # => [1, 2, 3]match({1, 2, 3}, List[int], lambda x: x) # => raises MatchErrormatch(range) (10), List[int], lambda x: x) # => が発生する MatchErrormatch([1, 2, 3], Set[int], lambda x: x) # => が発生するMatchErrormatch({1, 2, 3}, Set[int], lambda x: x) # => {1, 2, 3}match(range(10), Set[int], lambda x: x) # => MatchError が発生します
Callable の場合、注釈のない任意の引数は Any として扱われます。
def annotated(a: int, b: int) -> float:passdef not_annotated(a, b):passdef Partially_annotated(a, b: float):passmatch(annotated, Callable[[int, int], float], lambda x : x) # => annotatedmatch(not_annotated, Callable[[int, int], float], lambda x: x) # => は発生しますMatchErrormatch(not_annotated, Callable[[Any, Any], Any], lambda x: x) # => not_annotatedmatch(annotated, Callable[[Any, Any], Any], lambda x: x) # => 発生する MatchErrormatch(partially_annotated) , Callable[[Any, float], Any], lambda x: x) # => 部分的に注釈付き
TypeVar はサポートされていません。
パターンとして、任意の Python タイプ、任意のクラス、または任意の Python 値を使用できます。
演算子_
とint
やstr
などの組み込み型は、関数に渡される変数を抽出します。
タイプとクラスはinstanceof(value, pattern)
によって照合されます。
Iterable
パターンは、すべての要素を再帰的に照合します。 辞書も同様です。
パターン例 | それが何を意味するか | 一致する例 | 関数に渡される引数 | 一致しない例 |
---|---|---|---|---|
"hello" | 文字列"hello" のみが一致します | "hello" | 何もない | 他の値 |
None | のみNone | None | 何もない | 他の値 |
int | 任意の整数 | 42 | 42 | 他の値 |
float | 任意の浮動小数点数 | 2.35 | 2.35 | 他の値 |
str | 任意の文字列 | "hello" | "hello" | 他の値 |
tuple | 任意のタプル | (1, 2) | (1, 2) | 他の値 |
list | 任意のリスト | [1, 2] | [1, 2] | 他の値 |
MyClass | MyClass の任意のインスタンス。 MyClass を拡張するオブジェクト。 | MyClass() | そのインスタンス | 他のオブジェクト |
_ | 任意のオブジェクト (なしでも) | その価値観 | ||
ANY | _ と同じ | その価値観 | ||
(int, int) | 任意の 2 つの整数で構成されるタプル | (1, 2) | 1 と2 | (真、偽) |
[1, 2, _] | 1、2 で始まり、任意の値で終わるリスト | [1, 2, 3] | 3 | [1, 2, 3, 4] |
[1, 2, TAIL] | 1、2 で始まり任意のシーケンスで終わるリスト | [1, 2, 3, 4] | [3, 4] | [1, 7, 7, 7] |
{'type':'dog', age: _ } | type: "dog" と年齢を含む辞書 | {"type":"dog", "age": 3} | 3 | {"type":"cat", "age":2} |
{'type':'dog', age: int } | type: "dog" およびint age を持つ任意の辞書 | {"type":"dog", "age": 3} | 3 | {"type":"dog", "age":2.3} |
re.compile('(w+)-(w+)-cat$') | 正規表現 expr に一致する任意の文字列 | "my-fuffy-cat" | "my" と"puffy" | "fuffy-dog" |
Pet(name=_, age=7) | age == 7 の任意の Pet データクラス | Pet('rover', 7) | ['rover'] | Pet('rover', 8) |
Any | _ と同じ | その価値観 | ||
Union[int, float, None] | 任意の整数、浮動小数点数、またはなし | 2.35 | 2.35 | 他の値 |
Optional[int] | Union[int, None] と同じ | 2 | 2 | 他の値 |
Type[MyClass] | MyClass のサブクラス。 MyClass を拡張するクラス。 | MyClass | そのクラス | 他のオブジェクト |
Callable[[int], float] | まさにその署名を持つ呼び出し可能なもの | def a(q:int) -> float: ... | その機能 | def a(q) -> float: ... |
Tuple[MyClass, int, float] | (MyClass, int, float) と同じ | |||
Mapping[str, int] Mapping のサブタイプも受け入れ可能 | 文字列キーと整数値を使用したマッピングまたはマッピングのサブタイプ | {'a': 2, 'b': 3} | その辞書 | {'a': 'b', 'b': 'c'} |
Iterable[int] Iterable のサブタイプも受け入れ可能 | 整数値を持つ任意の反復可能または反復可能のサブタイプ | range(10) と[1, 2, 3] | その反復可能な | ['a', 'b', 'v'] |
デフォルトではmatch()
は厳密です。一致するパターンがない場合は、 MatchError
が発生します。
代わりに、何も一致しない場合に使用されるdefault
を使用してフォールバック値を提供できます。
>>> match([1, 2], [1, 2, 3], "whatever") MatchError: '_' not provided. This case is not handled: [1, 2] >>> match([1, 2], [1, 2, 3], "whatever", default=False) False
Pampy は Python の正規表現をサポートしています。コンパイルされた正規表現をパターンとして渡すことができ、Pampy はpattern.search()
を実行し、 .groups()
の結果をアクション関数に渡します。
def what_is(pet):return match(pet,re.compile('(w+)-(w+)-cat$'), ラムダ名, my: 'cat '+name,re.compile('(w+)-( w+)-dog$'), ラムダ名, my: 'dog '+name,_, "something else")what_is('fuffy-my-dog') # => 'dog fuffy'what_is('puffy-her-dog') # => '犬のパフィー'what_is('carla-your-cat') # => '猫の carla'what_is('roger-my-hamster') # => '何か他のもの」
Pampy は Python 3.6 以上で動作します。辞書マッチングは最新の Python でのみ動作するためです。
インストールするには:
$ pip install pampy
または$ pip3 install pampy
Pampy は Python3 ファーストですが、Manuel Barkhau によるこのバックポートを介して、その機能のほとんどを Python2 で使用できます。
pip install backports.pampy
backports.pampy からインポート一致、HEAD、TAIL、_