Pampy es bastante pequeño (150 líneas), razonablemente rápido y, a menudo, hace que su código sea más legible y, por lo tanto, más fácil de razonar. También existe una versión de JavaScript, llamada Pampy.js.
Los patrones se evalúan en el orden en que aparecen.
El operador _ significa "cualquier otro caso en el que no se me ocurrió".
de la coincidencia de importación de pampy, _def fibonacci(n):return match(n,1, 1,2, 1,_, lambda x: fibonacci(x-1) + fibonacci(x-2) )
de pampy import match, REST, _def lisp(exp):return match(exp,int, lambda x: x,callable, lambda x: x, (invocable, REST), lambda f, resto: f(*map(lisp, rest)),tupla, lambda t: list(map(lisp, t)), )más = lambda a, b: a + bmenos = lambda a, b: a - bfrom functools import reducelisp((más, 1, 2)) # => 3lisp((más, 1, (menos, 4, 2)) ) # => 3lisp((reducir, más, (rango, 10))) # => 45
match(x,3, "esto coincide con el número 3",int, "coincide con cualquier número entero", (str, int), lambda a, b: "una tupla (a, b) que puedes usar en una función", [1, 2, _], "cualquier lista de 3 elementos que comience con [1, 2]", {'x': _}, "cualquier dictado con una clave 'x' y cualquier valor asociado",_, "cualquier otra cosa")
de coincidencia de importación pampy, CABEZA, COLA, _x = [1, 2, 3]partida(x, [1, COLA], lambda t: t) # => [2, 3]partida(x, [CABEZA, COLA] , lambda h, t: (h, t)) # => (1, [2, 3])
TAIL
y REST
en realidad significan lo mismo.
de la coincidencia de importación de pampy, _x = [1, [2, 3], 4]match(x, [1, [_, 3], _], lambda a, b: [1, [a, 3], b] ) # => [1, [2, 3], 4]
mascota = { 'tipo': 'perro', 'detalles': { 'edad': 3 } }match(mascota, { 'detalles': { 'edad': _ } }, lambda edad: edad) # => 3match (mascota, { _ : { 'edad': _ } }, lambda a, b: (a, b)) # => ('detalles', 3)
Parece que poner varios _ dictados internos no debería funcionar. ¿No está garantizado realizar pedidos en dictados? Pero lo hace porque en Python 3.7, dict mantiene el orden de las claves de inserción de forma predeterminada.
clase Mascota: pasarclase Perro(Mascota): pasarclase Gato(Mascota): pasarclase Hámster(Mascota): pasardef what_is(x):return match(x,Perro, 'perro',Gato, 'gato',Mascota, 'cualquier otra mascota ', _, 'esto no es una mascota en absoluto', )what_is(Gato()) # => 'gato'what_is(Perro()) # => 'perro'what_is(Hamster()) # => 'cualquier otra mascota'what_is(Mascota()) # => 'cualquiera other pet'what_is(42) # => 'esto no es una mascota en absoluto'
Pampy admite clases de datos Python 3.7. Puede pasar el operador _
como argumento y coincidirá con esos campos.
@dataclassclass Mascota: nombre: strage: intpet = Pet('rover', 7)match(pet, Pet('rover', _), lambda edad: edad) # => 7match(pet, Pet(_, 7), nombre lambda: nombre) # => 'rover'match(mascota, mascota(_, _), nombre lambda, edad: (nombre, edad)) # => ('rover', 7)
Pampy admite escribir anotaciones.
clase Mascota: pasarclase Perro(Mascota): pasarclase Gato(Mascota): pasarclase Hámster(Mascota): pasartimestamp = NewType("año", Unión[int, float])def annotated(a: Tupla[int, float], b: str, c: E) -> marca de tiempo: passmatch((1, 2), Tupla[int, int], lambda a, b: (a, b)) # => (1, 2)match(1, Unión[cadena, int], lambda x: x) # => 1match('a', Unión[cadena, int], lambda x: x) # => 'a'match('a', Opcional[cadena], lambda x: x) # => 'a'match(Ninguno, Opcional[str], lambda x: x) # => Ningunomatch(Mascota, Tipo[Mascota], lambda x: x) # => Petmatch(Gato, Tipo[Mascota], lambda x: x) # => Catmatch(Perro, Cualquiera, lambda x: x) # => Dogmatch(Perro, Tipo[Cualquiera], lambda x: x) # => Dogmatch( 15, marca de tiempo, lambda x: x) # => 15match(10.0, marca de tiempo, lambda x: x) # => 10.0match([1, 2, 3], Lista[int], lambda x: x) # => [1, 2, 3]match({'a': 1, 'b': 2}, Dict[str, int], lambda x: x ) # => {'a': 1, 'b': 2}match(anotado, invocable[[Tupla[int, float], str, Pet], marca de tiempo], lambda x: x) # => anotado
Para los genéricos iterables, el tipo de valor real se adivina en función del primer elemento.
partido([1, 2, 3], Lista[int], lambda x: x) # => [1, 2, 3]partido([1, "b", "a"], Lista[int], lambda x: x) # => [1, "b", "a"]match(["a", "b", "c"], List[int], lambda x: x) # genera MatchErrormatch([" a", "b", "c"], Lista[Unión[str, int]], lambda x: x) # ["a", "b", "c"]match({"a": 1, "b": 2}, Dict[cadena, int], lambda x: x) # {"a": 1, "b": 2}match({"a": 1, "b": "perro"}, Dict[cadena, int] , lambdax: x) # {"a": 1, "b": "perro"}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) # genera MatchErrormatch({2: 1, 1: 2}, Dict[Unión[str, int], int], lambda x: x) # {2: 1, 1: 2}
Los genéricos iterables también coinciden con cualquiera de sus subtipos.
partido([1, 2, 3], Iterable[int], lambda x: x) # => [1, 2, 3]partido({1, 2, 3}, Iterable[int], lambda x: x) # => {1, 2, 3}coincidencia(rango(10), Iterable[int], lambda x: x) # => rango(10)coincidencia([1, 2, 3], Lista[int], lambda x: x) # => [1, 2, 3]match({1, 2, 3}, Lista[int], lambda x: x) # => plantea MatchErrormatch(range(10) , Lista[int], lambda x: x) # => plantea MatchErrormatch([1, 2, 3], Set[int], lambda x: x) # => plantea MatchErrormatch({1, 2, 3}, Set[int], lambda x: x) # => {1, 2, 3}match(range(10), Set[int], lambda x: x) # => genera MatchError
Para Callable, cualquier argumento sin anotación se trata como Cualquiera.
def anotado(a: int, b: int) -> float:passdef not_annotated(a, b):passdef parcialmente_annotated(a, b: float):passmatch(anotado, invocable[[int, int], float], lambda x : x) # => annotatedmatch(not_annotated, Callable[[int, int], float], lambda x: x) # => plantea MatchErrormatch(not_annotated, Callable[[Cualquiera, Cualquiera], Cualquiera], lambda x: x) # => not_annotatedmatch(anotado, Callable[[Cualquiera, Cualquiera], Cualquiera], lambda x: x) # => plantea MatchErrormatch(partially_annotated , Invocable[[Cualquiera, flotante], Cualquiera], lambda x: x) # => parcialmente_anotado
TypeVar no es compatible.
Como patrón puedes usar cualquier tipo de Python, cualquier clase o cualquier valor de Python.
El operador _
y los tipos integrados como int
o str
extraen variables que se pasan a funciones.
Los tipos y clases se combinan mediante instanceof(value, pattern)
.
Los patrones Iterable
coinciden recursivamente en todos sus elementos. Lo mismo ocurre con los diccionarios.
Ejemplo de patrón | lo que significa | Ejemplo coincidente | Argumentos pasados para funcionar. | Ejemplo NO coincidente |
---|---|---|---|---|
"hello" | sólo coincide la cadena "hello" | "hello" | nada | cualquier otro valor |
None | solo None | None | nada | cualquier otro valor |
int | Cualquier número entero | 42 | 42 | cualquier otro valor |
float | Cualquier número flotante | 2.35 | 2.35 | cualquier otro valor |
str | cualquier cadena | "hello" | "hello" | cualquier otro valor |
tuple | cualquier tupla | (1, 2) | (1, 2) | cualquier otro valor |
list | cualquier lista | [1, 2] | [1, 2] | cualquier otro valor |
MyClass | Cualquier instancia de MyClass. Y cualquier objeto que extienda MyClass. | MyClass() | esa instancia | cualquier otro objeto |
_ | Cualquier objeto (incluso Ninguno) | ese valor | ||
ANY | Lo mismo que _ | ese valor | ||
(int, int) | Una tupla formada por dos números enteros cualesquiera. | (1, 2) | 1 y 2 | (Verdadero, Falso) |
[1, 2, _] | Una lista que comienza con 1, 2 y termina con cualquier valor. | [1, 2, 3] | 3 | [1, 2, 3, 4] |
[1, 2, TAIL] | Una lista que comienza con 1, 2 y termina con cualquier secuencia. | [1, 2, 3, 4] | [3, 4] | [1, 7, 7, 7] |
{'type':'dog', age: _ } | Cualquier dictado con type: "dog" y con una edad | {"type":"dog", "age": 3} | 3 | {"type":"cat", "age":2} |
{'type':'dog', age: int } | Cualquier dictado con type: "dog" y con edad int | {"type":"dog", "age": 3} | 3 | {"type":"dog", "age":2.3} |
re.compile('(w+)-(w+)-cat$') | Cualquier cadena que coincida con esa expresión regular expr | "my-fuffy-cat" | "my" y "puffy" | "fuffy-dog" |
Pet(name=_, age=7) | Cualquier clase de datos de mascotas con age == 7 | Pet('rover', 7) | ['rover'] | Pet('rover', 8) |
Any | Lo mismo que _ | ese valor | ||
Union[int, float, None] | Cualquier número entero o flotante o Ninguno | 2.35 | 2.35 | cualquier otro valor |
Optional[int] | Lo mismo que Union[int, None] | 2 | 2 | cualquier otro valor |
Type[MyClass] | Cualquier subclase de MyClass. Y cualquier clase que extienda MyClass. | MyClass | esa clase | cualquier otro objeto |
Callable[[int], float] | Cualquier invocable con exactamente esa firma | def a(q:int) -> float: ... | esa función | def a(q) -> float: ... |
Tuple[MyClass, int, float] | Lo mismo que (MyClass, int, float) | |||
Mapping[str, int] Cualquier subtipo de Mapping también es aceptable | cualquier mapeo o subtipo de mapeo con claves de cadena y valores enteros | {'a': 2, 'b': 3} | ese dictado | {'a': 'b', 'b': 'c'} |
Iterable[int] Cualquier subtipo de Iterable también es aceptable | cualquier iterable o subtipo de iterable con valores enteros | range(10) y [1, 2, 3] | que iterable | ['a', 'b', 'v'] |
Por defecto, match()
es estricto. Si ningún patrón coincide, genera un MatchError
.
En su lugar, puede proporcionar un valor alternativo mediante default
que se utilizará cuando nada coincida.
>>> 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 es compatible con Regex de Python. Puede pasar una expresión regular compilada como patrón, y Pampy ejecutará pattern.search()
y luego pasará a la función de acción el resultado de .groups()
.
def what_is(pet):return match(pet,re.compile('(w+)-(w+)-cat$'), nombre lambda, my: 'cat '+nombre,re.compile('(w+)-( w+)-perro$'), nombre lambda, my: 'perro '+nombre,_, "algo más")what_is('fuffy-mi-perro') # => 'perro fuffy'what_is('puffy-su-perro') # => 'perro puffy'what_is('carla-tu-gato') # => 'gato carla'what_is('roger-my-hamster') # => ' otra cosa'
Pampy funciona en Python >= 3.6 Porque la coincidencia de dicts solo puede funcionar en las últimas versiones de Python.
Para instalarlo:
$ pip install pampy
o $ pip3 install pampy
Pampy es el primero en Python3, pero puedes usar la mayoría de sus funciones en Python2 a través de este backport de Manuel Barkhau:
pip install backports.pampy
de backports.pampy coincidencia de importación, CABEZA, COLA, _