Estoy tratando de entender la nueva sintaxis de coincidencia de patrones estructurales en Python 3.10. Entiendo que es posible hacer coincidir valores literales como este:
def handle(retcode): match retcode: case 200: print('success') case 404: print('not found') case _: print('unknown') handle(404) # not foundSin embargo, si refactorizo y muevo estos valores a variables de nivel de módulo, se produce un error porque las declaraciones ahora representan estructuras o patrones en lugar de valores:
SUCCESS = 200 NOT_FOUND = 404 def handle(retcode): match retcode: case SUCCESS: print('success') case NOT_FOUND: print('not found') case _: print('unknown') handle(404) # File "<ipython-input-2-fa4ae710e263>", line 6 # case SUCCESS: # ^ # SyntaxError: name capture 'SUCCESS' makes remaining patterns unreachable¿Hay alguna forma de usar la declaración de coincidencia para hacer coincidir los valores que se almacenan dentro de las variables?
La match de Python es mucho más que una simple declaración de cambio. Si usa solo lo que considera "nombres de variables", en realidad serán Patrones de captura . según definición en PEP no. 634
Además del hecho de que probablemente no debería usar la match para su caso de uso, debe usar nombres calificados (con puntos) de una de las siguientes maneras:
statuses = object() statuses.success = 200 status.not_found = 404 def handle(retcode): match retcode: case statuses.success: print("Success") case statuses.not_found: print("Not found") class StatusValues: success = 200 not_found = 404 def handle(retcode): match retcode: case StatusValues.success: print("Success") case StatusValues.not_found: print("Not found") Desarrollé la biblioteca match-ref que le permite acceder a cualquier variable local o global dentro o fuera de cualquier función, simplemente usando el ref. prefijo.
from matchref import ref import random SUCCESS = 200 NOT_FOUND = 404 def handle(retcode): random_code = random.randint(600,699) match retcode: case ref.SUCCESS: print("Success") case ref.NOT_FOUND: print("Not found") case ref.random_code: print("OK, you win!") Como puede ver, ref resolvió automáticamente las variables de sus espacios de nombres locales y globales (en este orden). No es necesaria ninguna configuración adicional.
Si no desea utilizar bibliotecas de terceros, puede ver una versión sin bibliotecas ligeramente similar a continuación.
locals() y globals() son funciones integradas en Python que devuelven un dict que contiene todos los nombres de variables asignados a sus respectivos valores. Debe poder acceder a los valores del dict utilizando la sintaxis punteada, ya que la match tampoco es compatible con la sintaxis de acceso al diccionario. Por lo tanto, puede escribir esta clase de ayuda simple:
class GetAttributeDict(dict): def __getattr__(self, name): return self[name]y usarlo así:
import random SUCCESS = 200 NOT_FOUND = 404 def handle(retcode): random_code = random.randint(600, 699) globs = GetAttributeDict(globals()) locs = GetAttributeDict(locals()) match retcode: case globs.SUCCESS: print("Success") case globs.NOT_FOUND: print("Not found") case locs.random_code: print("OK , you win!") Dado que parece tener la intención de reutilizar sus códigos de estado (porque de lo contrario podría alinearlos en sus case ), podría considerar usar módulos separados para esto.
constants.py:
SUCCESS = 200 NOT_FOUND = 404 main.py
import constants match retcode: case constants.SUCCESS: ... ... Una vez más, es posible que desee reconsiderar si desea utilizar la match en absoluto.
python > 3.10 le permite manejar patrones de casos de manera más eficiente.
| y if las declaraciones también se pueden usar.
usando |
match name: case "example_111" | "example_222": return f"Hello {name}" case _: return "Bye" usando la sentencia if
def get_product_info(make, in_dollar): match make: case "product_111" if in_dollar: return "10000 $" case "product_222" if not in_dollar: return "10000*73 INR" case _: return "error"Si la constante con la que está probando es un nombre con puntos, debe tratarse como una constante en lugar de como el nombre de la variable para colocar la captura (consulte PEP 636 # Coincidencia con constantes y enumeraciones ):
class Codes: SUCCESS = 200 NOT_FOUND = 404 def handle(retcode): match retcode: case Codes.SUCCESS: print('success') case Codes.NOT_FOUND: print('not found') case _: print('unknown') Aunque, dada la forma en que Python está tratando de implementar la coincidencia de patrones , creo que para situaciones como esta, probablemente sea un código más seguro y más claro usar una torre if/elif/else al comparar valores constantes.
Aparte del uso de valores literales , la sección Patrones de valor de PEP 635 menciona el uso de nombres con puntos o el uso de guardias . Vea a continuación para comparar:
valores literales
def handle(code): match code: case 200: print('success') case 404: print('not found') case _: print('unknown')Referencias:
nombres punteados
Cualquier nombre punteado (es decir, acceso de atributo) se interpreta como un patrón de valor.
class StatusCodes: OK = 200 NOT_FOUND = 404 def handle(code): match code: case StatusCodes.OK: print('success') case StatusCodes.NOT_FOUND: print('not found') case _: print('unknown')Referencias:
Guardias
[A] guard es una expresión arbitraria adjunta a un patrón y que debe evaluarse como un valor de "veracidad" para que el patrón tenga éxito.
SUCCESS = 200 NOT_FOUND = 404 def handle(code): match code: case status if status == SUCCESS: print('success') case status if status == NOT_FOUND: print('not found') case _: print('unknown')Referencias:
Espero poder ayudar a arrojar algo de luz sobre por qué los nombres simples funcionan de esta manera aquí.
En primer lugar, como ya han señalado otros, si necesita hacer coincidir los valores como parte de sus patrones, puede hacerlo de la siguiente manera:
Noneif )Me temo que nosotros (los autores de PEP) probablemente cometimos un pequeño error al incluir este fragmento de juguete en uno de los primeros tutoriales... desde entonces se ha vuelto un poco viral. Nuestro objetivo era liderar con el ejemplo más simple posible de coincidencia de patrones, pero en cambio parece que también creamos una primera impresión confusa para muchos (especialmente cuando se repite sin contexto).
La palabra que más se pasa por alto en el título de estos PEP es "estructural". Si no está haciendo coincidir la estructura del tema, la coincidencia de patrones estructurales probablemente no sea la herramienta adecuada para el trabajo.
El diseño de esta función se basó en la desestructuración (como el desempaquetado iterativo en el LHS de las asignaciones, pero generalizado para todos los objetos), por lo que facilitamos mucho la ejecución de la funcionalidad principal de extraer partes de un objeto y vincularlas a nombres También decidimos que sería útil permitir a los programadores hacer coincidir los valores, por lo que los agregamos (con la condición de que cuando se nombran los valores, deben calificarse con un punto, para distinguirlos de las extracciones más comunes ).
La coincidencia de patrones de Python nunca se diseñó realmente con la intención de impulsar declaraciones de cambio de estilo C como esta; eso ha sido propuesto para Python (y rechazado) dos veces antes, así que elegimos ir en una dirección diferente. Además, ya existe una forma obvia de activar un solo valor, que es más simple, más corta y funciona en todas las versiones de Python: ¡una buena escalera if / elif / else !
SUCCESS = 200 NOT_FOUND = 404 def handle(retcode): if retcode == SUCCESS: print('success') elif retcode == NOT_FOUND: print('not found') else: print('unknown') handle(404)(Si está realmente preocupado por el rendimiento o necesita una expresión, enviar desde un diccionario también es una buena alternativa).