
Arrêtez d’écrire des chaînes if-else Spaghetti : analyse de JSON avec le cas de correspondance de Python
Si vous travaillez dans la science des données, l’ingénierie des données ou en tant que développeur frontend/backend, vous traitez avec JSON. Pour les professionnels, seuls la mort, les impôts et l’analyse JSON sont inévitables. Le problème est que l’analyse de JSON est souvent très pénible.
Que vous extrayiez des données d’une API REST, analysiez des journaux ou lisiez des fichiers de configuration, vous vous retrouvez finalement avec un dictionnaire imbriqué que vous devez démêler. Et soyons honnêtes : le code que nous écrivons pour gérer ces dictionnaires est souvent… pour le moins laid.
Nous avons tous écrit le « Spaghetti Parser ». Vous connaissez celui-là. Cela commence par un simple if déclaration, mais vous devez ensuite vérifier si une clé existe. Ensuite, vous devez vérifier si la liste à l’intérieur de cette clé est vide. Ensuite, vous devez gérer un état d’erreur.
Avant de vous en rendre compte, vous disposez d’une tour de 40 lignes de if-elif-else des déclarations difficiles à lire et encore plus difficiles à maintenir. Les pipelines finiront par se briser en raison d’un cas extrême imprévu. Mauvaises vibrations partout !
Dans Python 3.10 sorti il y a quelques années, une fonctionnalité a été introduite que de nombreux data scientists n’ont toujours pas adoptée : Correspondance des modèles structurels avec match et case. Elle est souvent confondue avec une simple instruction « Switch » (comme en C ou Java), mais elle est beaucoup plus puissante. Il vous permet de vérifier le forme et structure de vos données, plutôt que simplement leur valeur.
Dans cet article, nous verrons comment remplacer vos vérifications de dictionnaire fragiles par des modèles élégants et lisibles en utilisant match et case. Je me concentrerai sur un cas d’utilisation spécifique que beaucoup d’entre nous connaissent, plutôt que d’essayer de donner un aperçu de la façon dont vous pouvez travailler avec match et case.
Le scénario : la réponse « mystère » de l’API
Imaginons un scénario typique. Vous interrogez une API externe sur laquelle vous n’avez pas un contrôle total. Disons, pour concrétiser le paramétrage, que l’API renvoie le statut d’un travail de traitement de données au format JSON. L’API est un peu incohérente (comme c’est souvent le cas).
Cela pourrait renvoyer un Succès réponse:
{
"status": 200,
"data": {
"job_id": 101,
"result": ["file_a.csv", "file_b.csv"]
}
}
Ou un Erreur réponse:
{
"status": 500,
"error": "Timeout",
"retry_after": 30
}
Ou peut-être une étrange réponse héritée qui n’est qu’une liste d’identifiants (car la documentation de l’API vous a menti) :
[101, 102, 103]
L’ancienne méthode : la if-else Pyramide du Destin
Si vous écriviez ceci en utilisant le flux de contrôle Python standard, vous vous retrouveriez probablement avec un codage défensif qui ressemble à ceci :
def process_response(response):
# Scenario 1: Standard Dictionary Response
if isinstance(response, dict):
status = response.get("status")
if status == 200:
# We have to be careful that 'data' actually exists
data = response.get("data", {})
results = data.get("result", [])
print(f"Success! Processed {len(results)} files.")
return results
elif status == 500:
error_msg = response.get("error", "Unknown Error")
print(f"Failed with error: {error_msg}")
return None
else:
print("Unknown status code received.")
return None
# Scenario 2: The Legacy List Response
elif isinstance(response, list):
print(f"Received legacy list with {len(response)} jobs.")
return response
# Scenario 3: Garbage Data
else:
print("Invalid response format.")
return None
Pourquoi le code ci-dessus fait-il mal à mon âme ?
- Il mélange « Quoi » avec « Comment » : Vous mélangez la logique métier (« Le succès signifie le statut 200 ») avec des outils de vérification de type comme
isinstance()et.get(). - C’est verbeux : Nous passons la moitié du code à vérifier simplement que les clés existent pour éviter un problème.
KeyError. - Difficile à numériser : Pour comprendre ce qui constitue un « succès », vous devez analyser mentalement plusieurs niveaux d’indentation imbriqués.
Une meilleure solution : la correspondance des modèles structurels
Entrez le match et case mots-clés.
Au lieu de poser des questions telles que « Est-ce un dictionnaire ? A-t-il une clé appelée statut ? Cette clé est-elle 200 ? », nous pouvons simplement décrire le forme des données que nous voulons traiter. Python tente d’adapter les données dans cette forme.
Voici exactement la même logique réécrite avec match et case:
def process_response_modern(response):
match response:
# Case 1: Success (Matches specific keys AND values)
case {"status": 200, "data": {"result": results}}:
print(f"Success! Processed {len(results)} files.")
return results
# Case 2: Error (Captures the error message and retry time)
case {"status": 500, "error": msg, "retry_after": time}:
print(f"Failed: {msg}. Retrying in {time}s...")
return None
# Case 3: Legacy List (Matches any list of integers)
case [first, *rest]:
print(f"Received legacy list starting with ID: {first}")
return response
# Case 4: Catch-all (The 'else' equivalent)
case _:
print("Invalid response format.")
return None
Notez qu’il est plus court de quelques lignes, mais ce n’est pas le seul avantage.
Pourquoi la correspondance des modèles structurels est géniale
Je peux trouver au moins trois raisons pour lesquelles les modèles structurels correspondent à match et case améliore la situation ci-dessus.
1. Déballage implicite des variables
Remarquez ce qui s’est passé dans Cas 1:
case {"status": 200, "data": {"result": results}}:
Nous n’avons pas seulement vérifié les clés. Nous avons vérifié simultanément que status est 200 ET extrait la valeur de result dans une variable nommée results.
Nous avons remplacé data = response.get("data").get("result") avec un simple placement variable. Si la structure ne correspond pas (par exemple, result est manquant), ce cas est simplement ignoré. Non KeyErrorpas de crash.
2. Modèle « Caractères génériques »
Dans Cas 2nous avons utilisé msg et time comme espaces réservés :
case {"status": 500, "error": msg, "retry_after": time}:
Cela indique à Python : j’attends un dictionnaire avec le statut 500, et quelques valeur correspondant aux touches "error" et "retry_after". Quelles que soient ces valeurs, liez-les aux variables msg et time pour que je puisse les utiliser immédiatement.
3. Déstructuration de liste
Dans Cas 3nous avons traité la réponse de la liste :
case [first, *rest]:
Ce modèle correspond n’importe lequel liste qui contient au moins un élément. Il lie le premier élément à first et le reste de la liste à rest. Ceci est incroyablement utile pour les algorithmes récursifs ou pour le traitement des files d’attente.
Ajout de « gardes » pour un contrôle supplémentaire
Parfois, adapter la structure ne suffit pas. Vous souhaitez assortir une structure seulement si une condition spécifique est remplie. Vous pouvez le faire en ajoutant un if clause directement au cas.
Imaginez que nous souhaitions traiter l’ancienne liste uniquement si elle contient moins de 10 éléments.
case [first, *rest] if len(rest) < 9:
print(f"Processing small batch starting with {first}")
Si la liste est trop longue, ce cas échoue et le code passe au cas suivant (ou au cas fourre-tout). _).
Conclusion
Je ne vous suggère pas de remplacer chaque simple if déclaration avec un match bloc. Cependant, vous devriez fortement envisager d’utiliser match et case quand tu es :
- Analyse des réponses de l’API : Comme indiqué ci-dessus, il s’agit du cas d’utilisation qui tue.
- Gestion des données polymorphes : Lorsqu’une fonction peut recevoir un
intunstrou undictet doit se comporter différemment pour chacun. - Traversée d’AST ou d’arbres JSON : Si vous écrivez des scripts pour récupérer ou nettoyer des données Web désordonnées.
En tant que professionnels des données, notre travail consiste souvent à nettoyer les données à 80 % et à les modéliser à 20 %. Tout ce qui rend la phase de nettoyage moins sujette aux erreurs et plus lisible constitue un gain considérable en termes de productivité.
Pensez à abandonner le if-else spaghetti. Laissez le match et case les outils font le gros du travail à la place.
Si vous êtes intéressé par l’IA, la science des données ou l’ingénierie des données, suivez-moi ou connectez-vous sur LinkedIn.



