52
votes

Comment utiliser les valeurs stockées dans les variables comme motifs de cas?

J'essaie de comprendre le nouveau appariement des modèles structurels Syntaxe à Python 3.10. Je comprends qu'il est possible de faire correspondre des valeurs littérales comme celle-ci:

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

Cependant, si je refacteur et déplace ces valeurs vers des variables au niveau du module, il en résulte une erreur car les instructions représentent désormais Structures ou modèles plutôt que valeurs:

def handle(retcode):
    match retcode:
        case 200:
            print('success')
        case 404:
            print('not found')
        case _:
            print('unknown')

handle(404)
# not found

Y a-t-il un moyen d'utiliser l'instruction MATCH pour faire correspondre les valeurs qui sont stockées dans les variables?


2 commentaires

Ma lecture de PEP-635 suggère que vous avez besoin d'un modèle de valeur, qui semble être défini comme un nom pointillé . Je ne sais pas pourquoi vous obtiendrez une erreur de syntaxe ici, car Success devrait être interprété comme un modèle de capture.


"Un nom non qualifié (c'est-à-dire un nom nu sans points) sera toujours interprété comme un modèle de capture"


5 Réponses :


45
votes

Si la constante contre laquelle vous testez est un nom pointillé, il doit être traité comme une constante plutôt que comme le nom de la variable pour mettre la capture (voir pep 636 # correspondance contre les constantes et enums ):

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')


3 commentaires

Je pense que cela n'est que moins clair car aucun corps n'est utilisé pour correspond aux instructions . Je soupçonne que l'implémentation peut effectuer une correspondance de motifs plus efficacement que la recherche linéaire imposée par une tour si .


@Chepner étant donné la spécification, il ne peut pas être plus rapide que la tour linéaire si (et c'est ainsi qu'il est implémenté actuellement). Les cas doivent être évalués en raison des règles de priorité


Pouvez-vous élaborer sur "Étant donné comment Python essaie d'implémenter la correspondance de modèles"? Qu'en est-il de la façon de le rendre moins sûr ou moins clair que si / autre?



16
votes

En plus d'utiliser des valeurs littérales , les Modèles de valeur La section de PEP 635 mentionne l'utilisation de noms pêchés ou l'utilisation de gardes . Voir ci-dessous pour comparaison:

valeurs littérales

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')

Références:

Noms pointillés

Tout nom en pointillé (c'est-à-dire l'accès à l'attribut) est interprété comme un modèle de valeur.

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')

gardiens

[A] Guard est une expression arbitraire attachée à un modèle et qui doit évaluer à une valeur de "vérité" pour que le modèle réussisse.

def handle(code):
    match code:
        case 200:
            print('success')
        case 404:
            print('not found')
        case _:
            print('unknown')


0 commentaires

29
votes

J'espère que je pourrai aider à éclairer pourquoi les noms nus fonctionnent de cette façon ici.

Tout d'abord, comme d'autres l'ont déjà noté, si vous devez faire correspondre les valeurs dans le cadre de vos modèles, vous peut le faire par:

  • correspondant aux littéraux pris en charge, comme les nombres, les chaînes, les booléens et aucun
  • Noms qualifiés (pointillés) correspondants
  • Utilisation de tests supplémentaires dans les gardes (qui sont séparés des modèles par si )

Je crains que nous (les auteurs de PEP) avons probablement fait une petite erreur en incluant cet extrait de jouet dans un tutoriel précoce ... il est depuis devenu un peu viral. Notre objectif était de mener avec l'exemple le plus simple possible de l'appariement des modèles, mais nous semblons plutôt avoir créé une première impression déroutante pour beaucoup (en particulier lorsqu'elle est répétée sans contexte).

Le mot le plus négligé Dans le titre de ces PEPS est "structurel". Si vous ne correspondez pas à la structure du sujet, la correspondance de modèle structurelle n'est probablement pas le bon outil pour le travail.

La conception de cette fonctionnalité a été motivée par la destruction (comme le déballage itérable sur le LHS des affectations, mais généralisé pour tous les objets), c'est pourquoi nous avons facilité l'exécution de la fonctionnalité de base de l'extraction de parties d'un objet et de les lier aux noms. Nous avons également décidé qu'il serait également utile de permettre aux programmeurs de correspondre aux valeurs, nous les avons donc ajoutés (avec la condition que lorsque les valeurs sont nommées, elles doivent être qualifiées avec un point, afin de les distinguer des extractions les plus courantes).

La correspondance du motif de Python n'a jamais été vraiment conçue avec l'intention de propulser des instructions de commutateur de style c comme celle-ci; Cela a été proposé pour Python (et rejeté) deux fois auparavant, nous avons donc choisi d'aller dans une direction différente. En outre, il existe déjà un moyen évident d'activer une seule valeur, ce qui est plus simple, plus court et fonctionne sur chaque version de Python: un bon si / elif / else échelle!

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 vous êtes vraiment préoccupé par les performances ou avez besoin d'une expression, l'envoi d'un dictionnaire est également une belle alternative .)


1 commentaires

Cette réponse m'a beaucoup clarifié. Je ne sais pas si les PEP sont modifiables une fois finaux, mais je pense qu'un point plus clair sur "Pourquoi ce n'est pas une déclaration de commutation / cas et ne devrait pas être utilisé comme un" pourrait éviter beaucoup de confusion. Au moins le "What est nouveau à Python à Python "Docs peut être modifié encore à coup sûr :)



6
votes

Python's Match est bien plus qu'une instruction Switch simple. Si vous utilisez Bare ce que vous considérez comme des "noms de variables", ils vont en fait être Capture motifs . selon la définition dans pep no. 634

En plus du fait que vous ne devriez probablement pas utiliser correspondant à pour votre cas d'utilisation, vous devez utiliser des noms qualifiés (pointillés) de l'une des manières suivantes:

# 1 objet plat

import constants

match retcode:
    case constants.SUCCESS: ...
    ...

# 2 Programmation orientée objet

SUCCESS = 200
NOT_FOUND = 404

# 3 Locaux qualifiés simples () / globals () Access

J'ai développé La bibliothèque Match-Ref qui vous permet d'accéder à toute variable locale ou globale en ou en dehors de toute fonction, en utilisant simplement le préfixe réf. .

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!")

Comme vous pouvez le voir, ref a automatiquement résolu des variables à partir de vos espaces de noms locaux et globaux (dans cet ordre). Il n'y a pas de configuration supplémentaire nécessaire.

Si vous ne souhaitez pas utiliser les bibliothèques tiertes, vous pouvez voir une version sans bibliothèque légèrement similaire ci-dessous.

# 4 Locaux qualifiés ( ) / Globalals () Accès sans libs tiers

globals ( ) sont des fonctions intégrées dans Python qui renvoient un dict contenant tous vos noms de variable mappés à leurs valeurs respectives. Vous devez être en mesure d'accéder aux valeurs du dict à l'aide de la syntaxe pointillée, car correspond à ne prend pas non plus en charge la syntaxe d'accès de Dictionary. Vous pouvez donc écrire cette classe d'assistance simple:

class GetAttributeDict(dict):
    def __getattr__(self, name):
        return self[name]

et l'utiliser comme:

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!")

# 5 Accès au module

Étant donné que vous semblez avoir l'intention de réutiliser vos codes d'état (car sinon vous pouvez les alimenter dans votre case ), vous pourriez envisager d'utiliser des modules distincts pour cela. p>

Constants.py:

class StatusValues:
    success = 200
    not_found = 404

def handle(retcode):
    match retcode:
        case StatusValues.success: print("Success")
        case StatusValues.not_found: print("Not found")

main.py

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")


0 commentaires

-1
votes

python> 3.10 vous permet de gérer plus efficacement les modèles de cas.

| et Si les instructions peuvent également être utilisées. p>

Utilisation |

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"

Utilisation Si Instruction

match name: 
    case "example_111" | "example_222": 
        return f"Hello {name}" 
    case _: 
        return "Bye"


0 commentaires