10.11.2018       Выпуск 255 (05.11.2018 - 11.11.2018)       Интересные проекты, инструменты, библиотеки

### Экспериментальная функция:

Ниже вы видите текст статьи по ссылке. По нему можно быстро понять ссылка достойна прочтения или нет

Просим обратить внимание, что текст по ссылке и здесь может не совпадать.

# Pampy: Pattern Matching for Python

Pampy is pretty small (150 lines), reasonably fast, and often makes your code more readable, and easier to reason about.

## You can write many patterns

Patterns are evaluated in the order they appear.

## You can write Fibonacci

The operator _ means "any other case I didn't think of".

```from pampy import match, _

def fibonacci(n):
return match(n,
1, 1,
2, 1,
_, lambda x: fibonacci(x-1) + fibonacci(x-2)
)```

## You can write a Lisp calculator in 5 lines

```from pampy import match, REST, _

def lisp(exp):
return match(exp,
int,                lambda x: x,
callable,           lambda x: x,
(callable, REST),   lambda f, rest: f(*map(lisp, rest)),
tuple,              lambda t: list(map(lisp, t)),
)

plus = lambda a, b: a + b
minus = lambda a, b: a - b
from functools import reduce

lisp((plus, 1, 2))                 # => 3
lisp((plus, 1, (minus, 4, 2)))     # => 3
lisp((reduce, plus, (1, 2, 3)))     # => 6```

## You can match so many things!

```match(x,
3,              "this matches the number 3",

int,            "matches any integer",

(str, int),     lambda a, 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]

```from pampy import match, HEAD, TAIL, _

x = [1, 2, 3]

match(x, [1, TAIL],     lambda t: t)            # => [2, 3]

match(x, [HEAD, TAIL],  lambda h, t: (h, t))    # => (1, [2, 3])
```

`TAIL` and `REST` actually mean the same thing.

## You can nest lists and tuples

```from pampy import match, _

x = [1, [2, 3], 4]

match(x, [1, [_, 3], _], lambda a, 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': _ } }, lambda age: age)        # => 3

match(pet, { _ : { 'age': _ } },        lambda a, 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 because in Python 3.7, dict is an OrderedDict by default

## You can match class hierarchies

```class Pet:          pass
class Dog(Pet):     pass
class Cat(Pet):     pass
class Hamster(Pet): pass

def what_is(x):
return match(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 match

As Pattern you can use any Python type, any class, or any Python value.

The operator `_` and types like `int` or `str`, extract variables that are passed to functions.

Types and Classes are matched via `instanceof(value, pattern)`.

`Iterable` Patterns 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 value
`None`only `None``None`nothingany other value
`int`Any integer`42``42`any other value
`float`Any float number`2.35``2.35`any other value
`str`Any string`"hello"``"hello"`any other value
`tuple`Any tuple`(1, 2)``(1, 2)`any other value
`list`Any list`[1, 2]``[1, 2]`any other value
`MyClass`Any instance of MyClass. And any object that extends MyClass.`MyClass()`that instanceany other object
`_`Any object (even None)that value
`ANY`The same as `_`that value
`(int, int)`A tuple made of any two integers`(1, 2)``1` and `2`(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 with `type: "dog"` and with an age`{"type":"dog", "age": 3}``3``{"type":"cat", "age":2}`
`{'type':'dog', age: int }`Any dict with `type: "dog"` and with an `int` age`{"type":"dog", "age": 3}``3``{"type":"dog", "age":2.3}`

## Using strict=False

By default `match()` is strict. If no pattern matches, it raises a `MatchError`.

You can prevent it using `strict=False`. In this case `match` just returns `False` if 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)
False
``````

## Install

Currently it works only in Python > 3.6 Because 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 pampy`

or `\$ pip3 install pampy`