Package: backports.pampy
Pampy: Pattern Matching for PythonPampy is pretty small (150 lines), reasonably fast, and often makes your code more readable
and hence easier to reason about.There is also a JavaScript version, called Pampy.js.You can write many patternsPatterns are evaluated in the order they appear.You can write FibonacciThe operator _ means "any other case I didn't think of".frompampyimportmatch,_deffibonacci(n):returnmatch(n,1,1,2,1,_,lambdax:fibonacci(x-1)+fibonacci(x-2))You can write a Lisp calculator in 5 linesfrompampyimportmatch,REST,_deflisp(exp):returnmatch(exp,int,lambdax:x,callable,lambdax:x,(callable,REST),lambdaf,rest:f(*map(lisp,rest)),tuple,lambdat:list(map(lisp,t)),)plus=lambdaa,b:a+bminus=lambdaa,b:a-bfromfunctoolsimportreducelisp((plus,1,2))# => 3lisp((plus,1,(minus,4,2)))# => 3lisp((reduce,plus,(range,10)))# => 45You can match so many things!match(x,3,"this matches the number 3",int,"matches any integer",(str,int),lambdaa,b:"a tuple (a, b) you can use in a function",[1,2,_],"any list of 3 elements that begins with [1, 2]",{'x':_},"any dict with a key 'x' and any value associated",_,"anything else")You can match [HEAD, TAIL]frompampyimportmatch,HEAD,TAIL,_x=[1,2,3]match(x,[1,TAIL],lambdat:t)# => [2, 3]match(x,[HEAD,TAIL],lambdah,t:(h,t))# => (1, [2, 3])TAILandRESTactually mean the same thing.You can nest lists and tuplesfrompampyimportmatch,_x=[1,[2,3],4]match(x,[1,[_,3],_],lambdaa,b:[1,[a,3],b])# => [1, [2, 3], 4]You can nest dicts. And you can use _ as key!pet={'type':'dog','details':{'age':3}}match(pet,{'details':{'age':_}},lambdaage:age)# => 3match(pet,{_:{'age':_}},lambdaa,b:(a,b))# => ('details', 3)It feels like putting multiple _ inside dicts shouldn't work. Isn't ordering in dicts not guaranteed ?
But it does becausein Python 3.7, dict maintains insertion key order by defaultYou can match class hierarchiesclassPet:passclassDog(Pet):passclassCat(Pet):passclassHamster(Pet):passdefwhat_is(x):returnmatch(x,Dog,'dog',Cat,'cat',Pet,'any other pet',_,'this is not a pet at all',)what_is(Cat())# => 'cat'what_is(Dog())# => 'dog'what_is(Hamster())# => 'any other pet'what_is(Pet())# => 'any other pet'what_is(42)# => 'this is not a pet at all'All the things you can matchAs Pattern you can use any Python type, any class, or any Python value.The operator_and built-in types likeintorstr, extract variables that are passed to functions.Types and Classes are matched viainstanceof(value, pattern).IterablePatterns match recursively through all their elements.  The same goes for dictionaries.Pattern ExampleWhat it meansMatched ExampleArguments Passed to functionNOT Matched Example"hello"only the string"hello"matches"hello"nothingany other valueNoneonlyNoneNonenothingany other valueintAny integer4242any other valuefloatAny float number2.352.35any other valuestrAny string"hello""hello"any other valuetupleAny tuple(1, 2)(1, 2)any other valuelistAny list[1, 2][1, 2]any other valueMyClassAny instance of MyClass.And any object that extends MyClass.MyClass()that instanceany other object_Any object (even None)that valueANYThe same as_that value(int, int)A tuple made of any two integers(1, 2)1and2(True, False)[1, 2, _]A list that starts with 1, 2 and ends with any value[1, 2, 3]3[1, 2, 3, 4][1, 2, TAIL]A list that start with 1, 2 and ends with any sequence[1, 2, 3, 4][3, 4][1, 7, 7, 7]{'type':'dog', age: _ }Any dict withtype: "dog"and with an age{"type":"dog", "age": 3}3{"type":"cat", "age":2}{'type':'dog', age: int }Any dict withtype: "dog"and with anintage{"type":"dog", "age": 3}3{"type":"dog", "age":2.3}re.compile('(\w+)-(\w+)-cat$')Any string that matches that regular expression expr"my-fuffy-cat""my"and"puffy""fuffy-dog"Pet(name=_, age=7)Any Pet dataclass withage == 7Pet('rover', 7)['rover']Pet('rover', 8)Using strict=FalseBy defaultmatch()is strict. If no pattern matches, it raises aMatchError.You can prevent it usingstrict=False. In this casematchjust returnsFalseif nothing matches.>>> match([1, 2], [1, 2, 3], "whatever")
MatchError: '_' not provided. This case is not handled: [1, 2]

>>> match([1, 2], [1, 2, 3], "whatever", strict=False)
FalseUsing Regular ExpressionsPampy supports Python's Regex. You can pass a compiled regex as pattern, and Pampy is going to runpatter.search(), and then pass to the action function the result of.groups().defwhat_is(pet):returnmatch(pet,re.compile('(\w+)-(\w+)-cat$'),lambdaname,my:'cat '+name,re.compile('(\w+)-(\w+)-dog$'),lambdaname,my:'dog '+name,_,"something else")what_is('fuffy-my-dog')# => 'dog fuffy'what_is('puffy-her-dog')# => 'dog puffy'what_is('carla-your-cat')# => 'cat carla'what_is('roger-my-hamster')# => 'something else'Using DataclassesPampy supports Python 3.7 dataclasses. You can pass the operator_as arguments and it will match those fields.@dataclassclassPet:name:strage:intpet=Pet('rover',7)match(pet,Pet('rover',_),lambdaage:age)# => 7match(pet,Pet(_,7),lambdaname:name)# => 'rover'match(pet,Pet(_,_),lambdaname,age:(name,age))# => ('rover', 7)InstallCurrently it works only in Python >= 3.6Because dict matching can work only in the latest Pythons.I'm currently working on a backport with some minor syntax changes for Python2.To install it:$ pip install pampyor$ pip3 install pampy