{
  "assessmentTests": {
    "python_test": {
      "name": "Test Python",
      "desc": "30 questions de scénarios sur la syntaxe, les fonctions, les structures de données et le comportement des objets — découvrez si votre Python correspond à ce qu'une offre d'emploi entend par maîtrise de Python.",
      "recommendation": "Votre profil de compétences Python",
      "results": {
        "beginner": {
          "name": "Débutant",
          "desc": "Vous pouvez suivre un court script et modifier les valeurs à l'intérieur, mais les questions que vous avez manquées se regroupent autour du comportement spécifique du langage plutôt que de la logique générale — un argument par défaut créé une seule fois et réutilisé, is par rapport à == sur deux listes identiques, une variable qui devient locale dès que vous lui assignez une valeur n'importe où dans une fonction. Rien de tout cela n'a à voir avec le fait de mal programmer ; ce sont des règles spécifiques à Python qui trompent les personnes venant d'autres langages tout autant que les débutants. Elles importent au travail car chacune est un endroit où un bug se cache dans du code qui semble correct au premier coup d'œil.",
          "recommendation": "Commencez par trois choses, dans cet ordre : les arguments par défaut mutables (pourquoi basket=[] est dangereux), la différence entre is et ==, et comment fonctionne la portée des variables à l'intérieur d'une fonction. Le tutoriel Python officiel sur docs.python.org couvre tous les trois directement, et le terme « Python gotchas » trouve des exemples détaillés de chacun."
        },
        "intermediate": {
          "name": "Intermédiaire",
          "desc": "Vous gérez confortablement les scripts du quotidien — structures de données, contrôle de flux, fonctions simples — et vous ne seriez pas ralenti par les travaux de maintenance courants ou une petite fonctionnalité sur une base de code existante. L'écart entre ici et Avancé concerne surtout ce qui se passe quand les fonctionnalités pratiques de Python interagissent entre elles : une copie superficielle laissant des données imbriquées partagées quand vous vous attendiez à une duplication, la valeur de retour d'un générateur qui ne s'ajoute pas silencieusement à sa sortie, les décorateurs qui s'empilent dans un ordre facile à inverser. Ce sont les genres de bugs qu'un examinateur senior signale en dix secondes et que tous les autres passent une heure à traquer.",
          "recommendation": "Concentrez-vous sur les internals des objets et itérations : copie superficielle par rapport à copie profonde, ce qu'un générateur fait réellement avec return, et comment fonctionne l'ordre d'empilement des décorateurs. Puis les bases des classes que la plupart des tutoriels sautent — égalité par défaut sans __eq__, et pourquoi un attribut de classe mutable est partagé entre toutes les instances."
        },
        "advanced": {
          "name": "Avancé",
          "desc": "C'est le niveau que la plupart des offres d'emploi entendent par « fort en Python ». Vous allez chercher le bon outil sans le chercher partout, vous savez pourquoi un élément d'état partagé a changé plutôt que seulement que c'est le cas, et vous pouvez lire la fonction de quelqu'un d'autre et prédire correctement sa sortie. Ce qui sépare cette bande du sommet est le côté défensif du travail : savoir lequel de deux approches correctes se comportera mal une fois qu'un attribut de classe, un argument nommé obligatoire, ou une hiérarchie d'exceptions est impliqué.",
          "recommendation": "Progressez dans les parties qui protègent une base de code que d'autres gens touchent aussi : les arguments nommés obligatoires comme un choix de conception d'API plutôt qu'une curiosité syntaxique, la hiérarchie d'exceptions (pour que les clauses except attrapent la bonne famille d'erreur, et pas seulement une classe), et les gestionnaires de contexte comme la façon standard de garantir le nettoyage. Les articles approfondis de Real Python sont une bonne prochaine étape pour tous les trois."
        },
        "expert": {
          "name": "Expert",
          "desc": "Vous avez obtenu un score aux premier rangs de chaque section — syntaxe centrale, fonctions et portée, structures de données, et idiomes orientés objet. Pratiquement, cela signifie que vous pouvez vous voir remettre le fichier d'un étranger et expliquer pourquoi il produit la sortie qu'il fait, non seulement ce que la syntaxe dit qu'il devrait faire, ce qui est la compétence plus difficile et plus précieuse. À ce niveau, le langage brut est rarement le facteur limitant de votre travail ; la limite est généralement la taille ou la forme du système autour du code.",
          "recommendation": "Les rendements se font maintenant en outillage et en examen : la vérification de type statique avec mypy ou pyright, le profilage avant l'optimisation plutôt que la conjecture, et la lecture des pull requests d'autres personnes pour exactement cette catégorie de bug — ceux qui passent tous les tests et font toujours la mauvaise chose sous une entrée spécifique. Si vous êtes filtré pour un rôle, décrivez un bug comme celui-ci que vous avez trouvé plutôt que de nommer des fonctionnalités du langage — cela démontre le raisonnement, pas seulement le vocabulaire."
        }
      },
      "questions": [
        {
          "question": "Vous écrivez def add_item(item, basket=[]): basket.append(item); return basket, puis vous appelez add_item('a') et add_item('b'), en laissant basket de côté les deux fois. Qu'est-ce que le deuxième appel retourne ?",
          "options": [
            {
              "icon": "",
              "label": "['b'] — chaque appel obtient une liste vide fraîche par défaut"
            },
            {
              "icon": "",
              "label": "TypeError — vous devez fournir un argument basket"
            },
            {
              "icon": "",
              "label": "['a', 'b'] — la liste par défaut est créée une fois et réutilisée entre les appels"
            },
            {
              "icon": "",
              "label": "['a'] puis ['b'] comme deux listes indépendantes à un seul élément"
            }
          ]
        },
        {
          "question": "a = [1, 2, 3] et b = [1, 2, 3]. Qu'est-ce que a == b retourne, et qu'est-ce que a is b retourne ?",
          "options": [
            {
              "icon": "",
              "label": "True pour les deux — les listes identiques sont toujours le même objet"
            },
            {
              "icon": "",
              "label": "True pour ==, False pour is — contenus égaux mais objets différents en mémoire"
            },
            {
              "icon": "",
              "label": "False pour les deux — les littéraux séparés ne sont jamais égaux en Python"
            },
            {
              "icon": "",
              "label": "True pour is, False pour == — is vérifie l'égalité des valeurs pour les listes"
            }
          ]
        },
        {
          "question": "En Python 3, à quoi évalue 7 / 2, et à quoi évalue 7 // 2 ?",
          "options": [
            {
              "icon": "",
              "label": "3.5, puis 3 — / retourne toujours un float, // arrondit le résultat"
            },
            {
              "icon": "",
              "label": "3, puis 3.5 — / tronque et // retourne un float"
            },
            {
              "icon": "",
              "label": "3.5, puis 3.5 — les deux opérateurs retournent un float en Python 3"
            },
            {
              "icon": "",
              "label": "3, puis 3 — les deux opérateurs arrondissent le résultat en Python 3"
            }
          ]
        },
        {
          "question": "s = 'hello', puis vous exécutez s[0] = 'H'. Que se passe-t-il ?",
          "options": [
            {
              "icon": "",
              "label": "s devient 'Hello'"
            },
            {
              "icon": "",
              "label": "s devient 'Hhello' car le caractère est inséré, non remplacé"
            },
            {
              "icon": "",
              "label": "TypeError — les chaînes sont immuables et ne supportent pas l'assignation d'élément"
            },
            {
              "icon": "",
              "label": "Rien — l'assignation est silencieusement ignorée et s reste 'hello'"
            }
          ]
        },
        {
          "question": "x = [10, 20, 30, 40, 50]. Qu'est-ce que x[1:4] retourne ?",
          "options": [
            {
              "icon": "",
              "label": "[20, 30, 40, 50]"
            },
            {
              "icon": "",
              "label": "[10, 20, 30, 40]"
            },
            {
              "icon": "",
              "label": "[20, 30, 40]"
            },
            {
              "icon": "",
              "label": "[30, 40]"
            }
          ]
        },
        {
          "question": "name = 'Ana' et age = 30. Qu'est-ce que f'{name} is {age}' produit ?",
          "options": [
            {
              "icon": "",
              "label": "{name} is {age}"
            },
            {
              "icon": "",
              "label": "Ana is 30"
            },
            {
              "icon": "",
              "label": "name is age"
            },
            {
              "icon": "",
              "label": "Un TypeError, car les f-strings nécessitent la méthode format()"
            }
          ]
        },
        {
          "question": "À quoi 1 < 2 < 3 évalue-t-il ?",
          "options": [
            {
              "icon": "",
              "label": "True — Python enchaîne les comparaisons comme (1 < 2) and (2 < 3)"
            },
            {
              "icon": "",
              "label": "False — les comparaisons enchaînées sont évaluées de gauche à droite sans court-circuitage, donnant 1 < 2 < 3 -> 1 < True -> False"
            },
            {
              "icon": "",
              "label": "Un SyntaxError — les comparaisons ne peuvent pas être enchaînées"
            },
            {
              "icon": "",
              "label": "3 — Python retourne la dernière valeur de la chaîne"
            }
          ]
        },
        {
          "question": "À quoi bool([]) retourne, et à quoi bool([0]) retourne ?",
          "options": [
            {
              "icon": "",
              "label": "False, puis False — toute liste contenant seulement des valeurs falsy est falsy"
            },
            {
              "icon": "",
              "label": "False, puis True — une liste vide est falsy, une liste contenant un élément (même 0) est truthy"
            },
            {
              "icon": "",
              "label": "True, puis True — les objets non-None sont toujours truthy"
            },
            {
              "icon": "",
              "label": "True, puis False — une liste est truthy seulement quand elle est vide"
            }
          ]
        },
        {
          "question": "Un count = 0 au niveau du module existe. À l'intérieur de def increment(): count += 1; return count, vous appelez increment(). Que se passe-t-il ?",
          "options": [
            {
              "icon": "",
              "label": "UnboundLocalError — assigner à count n'importe où dans la fonction le rend local, donc count += 1 essaie de lire une variable locale avant qu'elle soit assignée"
            },
            {
              "icon": "",
              "label": "Il retourne 1 et incrémente le count au niveau du module à 1"
            },
            {
              "icon": "",
              "label": "Il retourne 1 mais laisse le count au niveau du module à 0"
            },
            {
              "icon": "",
              "label": "Il lève NameError car count n'a jamais été déclaré à l'intérieur de la fonction"
            }
          ]
        },
        {
          "question": "def f(*args, **kwargs): return args, kwargs. Qu'est-ce que f(1, 2, x=3) retourne ?",
          "options": [
            {
              "icon": "",
              "label": "([1, 2], {'x': 3})"
            },
            {
              "icon": "",
              "label": "((1, 2), {'x': 3})"
            },
            {
              "icon": "",
              "label": "(1, 2, {'x': 3})"
            },
            {
              "icon": "",
              "label": "TypeError — les arguments positionnels et nommés ne peuvent pas être mélangés dans un appel"
            }
          ]
        },
        {
          "question": "Vous construisez funcs = [lambda: i for i in range(3)] dans une boucle. Qu'est-ce que [f() for f in funcs] retourne ?",
          "options": [
            {
              "icon": "",
              "label": "[2, 2, 2] — chaque lambda cherche i quand elle est appelée, et à ce moment la boucle est terminée avec i égal à 2"
            },
            {
              "icon": "",
              "label": "[0, 1, 2] — chaque lambda capture la valeur de i au moment de sa création"
            },
            {
              "icon": "",
              "label": "[0, 0, 0] — les lambdas capturent toujours la première valeur assignée à une variable"
            },
            {
              "icon": "",
              "label": "Un NameError, car i n'existe plus une fois que la boucle se termine"
            }
          ]
        },
        {
          "question": "Une fonction est définie avec @a directement au-dessus de @b, qui est directement au-dessus de def f(): pass. Dans quel ordre les décorateurs sont-ils appliqués à f ?",
          "options": [
            {
              "icon": "",
              "label": "b enveloppe f en premier, puis a enveloppe le résultat de b — le décorateur le plus proche de la fonction s'exécute en premier"
            },
            {
              "icon": "",
              "label": "a enveloppe f en premier, puis b enveloppe le résultat — les décorateurs s'appliquent de haut en bas"
            },
            {
              "icon": "",
              "label": "Les deux décorateurs enveloppent f indépendamment et seul le dernier défini a un effet"
            },
            {
              "icon": "",
              "label": "Cela dépend de l'ordre dans lequel les décorateurs sont importés, pas leur position dans le code"
            }
          ]
        },
        {
          "question": "def f(a, *, b): return a + b. Lequel de ces appels fonctionne ?",
          "options": [
            {
              "icon": "",
              "label": "f(1, b=2) — b doit être passé par mot-clé car il vient après le * nu dans la signature"
            },
            {
              "icon": "",
              "label": "f(1, 2) — les arguments positionnels remplissent les paramètres de gauche à droite indépendamment du *"
            },
            {
              "icon": "",
              "label": "f(a=1, 2) — les arguments nommés doivent venir en premier"
            },
            {
              "icon": "",
              "label": "f(1) — b est optionnel car il suit le *"
            }
          ]
        },
        {
          "question": "Une fonction générateur yield 1, puis yield 2, puis fait return 99. Qu'est-ce que list(the_generator()) contient, et qu'advient-il du 99 ?",
          "options": [
            {
              "icon": "",
              "label": "[1, 2, 99] — return à l'intérieur d'un générateur se comporte comme un yield de plus"
            },
            {
              "icon": "",
              "label": "[99] — return remplace immédiatement toutes les valeurs déjà yieldées"
            },
            {
              "icon": "",
              "label": "[1, 2] — la valeur de retour d'un générateur devient la valeur attachée au StopIteration qu'il lève, elle n'est pas yieldée"
            },
            {
              "icon": "",
              "label": "Un RuntimeError, car les générateurs ne peuvent pas contenir d'instruction return"
            }
          ]
        },
        {
          "question": "Dans une fonction outer(), x = 1 est défini, puis un inner() imbriqué fait nonlocal x; x += 1, et outer() appelle inner() avant de retourner x. Qu'est-ce que outer() retourne ?",
          "options": [
            {
              "icon": "",
              "label": "1 — inner() opère sur sa propre copie locale de x, donc le changement ne persiste pas"
            },
            {
              "icon": "",
              "label": "Un UnboundLocalError, car x n'est pas déclaré à l'intérieur de inner() avant la déclaration nonlocal"
            },
            {
              "icon": "",
              "label": "Un SyntaxError — nonlocal ne peut être utilisé que à l'intérieur des classes"
            },
            {
              "icon": "",
              "label": "2 — nonlocal permet à inner() de modifier le x de la fonction externe au lieu de créer une nouvelle variable locale"
            }
          ]
        },
        {
          "question": "d = {'a': 1, 'b': 2}. Qu'est-ce que d.get('c', 0) retourne, et est-ce qu'il ajoute 'c' au dictionnaire ?",
          "options": [
            {
              "icon": "",
              "label": "0, et non — get() retourne la valeur par défaut sans insérer la clé"
            },
            {
              "icon": "",
              "label": "0, et oui — get() insère la clé avec la valeur par défaut"
            },
            {
              "icon": "",
              "label": "KeyError, car 'c' n'existe pas"
            },
            {
              "icon": "",
              "label": "None, et oui — get() ajoute toujours les clés manquantes avec une valeur None"
            }
          ]
        },
        {
          "question": "t = (1, 2, [3, 4]). Pouvez-vous exécuter t[2].append(5), et pouvez-vous exécuter t[0] = 10 ?",
          "options": [
            {
              "icon": "",
              "label": "Les deux fonctionnent — l'imbrication d'une liste à l'intérieur d'un tuple rend le tuple entier mutable"
            },
            {
              "icon": "",
              "label": "Les deux lèvent TypeError — rien à l'intérieur d'un tuple ne peut jamais être modifié"
            },
            {
              "icon": "",
              "label": "t[2].append(5) lève TypeError, mais t[0] = 10 fonctionne car les tuples permettent la réassignation de leur premier élément"
            },
            {
              "icon": "",
              "label": "t[2].append(5) fonctionne car la liste à l'intérieur est mutable ; t[0] = 10 lève TypeError car le tuple lui-même est immuable"
            }
          ]
        },
        {
          "question": "Lequel de ceux-ci retourne un itérateur qui calcule les valeurs paresseusement plutôt que de construire la séquence entière en mémoire en une seule fois : [x*x for x in range(1000000)] ou (x*x for x in range(1000000)) ?",
          "options": [
            {
              "icon": "",
              "label": "Celui avec des parenthèses, (x*x for x in range(1000000)) — c'est une expression de générateur"
            },
            {
              "icon": "",
              "label": "Celui avec des crochets — les compréhensions de liste sont toujours paresseuses en Python 3"
            },
            {
              "icon": "",
              "label": "Les deux sont paresseux — la seule différence est le type retourné à la fin"
            },
            {
              "icon": "",
              "label": "Aucun — les deux construisent la séquence complète en mémoire immédiatement"
            }
          ]
        },
        {
          "question": "Un bloc try divise par zéro, la clause except ZeroDivisionError imprime « a », et une clause finally imprime « b ». Qu'est-ce qui imprime, et dans quel ordre ?",
          "options": [
            {
              "icon": "",
              "label": "b puis a — finally s'exécute toujours avant toute clause except"
            },
            {
              "icon": "",
              "label": "a puis b — la clause except correspondante s'exécute en premier, et finally s'exécute toujours après indépendamment"
            },
            {
              "icon": "",
              "label": "Seulement b — l'exception n'est pas réellement capturée, donc « a » n'imprime jamais"
            },
            {
              "icon": "",
              "label": "Seulement a — finally est ignoré une fois qu'une exception a déjà été gérée"
            }
          ]
        },
        {
          "question": "import copy; a = [[1, 2], [3, 4]]; b = copy.copy(a); b[0].append(99). Est-ce que a[0] change aussi ?",
          "options": [
            {
              "icon": "",
              "label": "Non — copy.copy() duplique toujours aussi les objets imbriqués"
            },
            {
              "icon": "",
              "label": "Non — l'ajout à b[0] crée une toute nouvelle liste pour b[0] seulement"
            },
            {
              "icon": "",
              "label": "Oui, mais seulement parce que copy.copy() a été appelé sans l'argument deepcopy"
            },
            {
              "icon": "",
              "label": "Oui — copy.copy() fait une copie superficielle, donc les listes imbriquées sont toujours partagées entre a et b"
            }
          ]
        },
        {
          "question": "d = {'x': 1, 'y': 2}. Qu'est-ce que 'x' in d vérifie — les clés, les valeurs, ou les deux ?",
          "options": [
            {
              "icon": "",
              "label": "Les valeurs — `in` vérifie si 1 ou 2 égale 'x'"
            },
            {
              "icon": "",
              "label": "Tant les clés que les valeurs sont vérifiées"
            },
            {
              "icon": "",
              "label": "Ni l'un ni l'autre — `in` sur un dictionnaire lève toujours TypeError"
            },
            {
              "icon": "",
              "label": "Les clés — `in` sur un dictionnaire teste l'appartenance parmi ses clés, pas ses valeurs"
            }
          ]
        },
        {
          "question": "names = ['Al', 'Bo'] et scores = [10, 20]. Qu'est-ce que list(zip(names, scores)) retourne ?",
          "options": [
            {
              "icon": "",
              "label": "[('Al', 'Bo'), (10, 20)]"
            },
            {
              "icon": "",
              "label": "['Al', 10, 'Bo', 20]"
            },
            {
              "icon": "",
              "label": "[('Al', 10), ('Bo', 20)]"
            },
            {
              "icon": "",
              "label": "Un ValueError, car zip nécessite trois arguments ou plus"
            }
          ]
        },
        {
          "question": "class Dog: tricks = [] contient une liste au niveau de la classe, et add_trick ajoute à self.tricks. d1 = Dog() et d2 = Dog() sont tous deux créés, puis d1.add_trick('sit') s'exécute. Qu'est-ce que d2.tricks contient ?",
          "options": [
            {
              "icon": "",
              "label": "[] — chaque instance obtient sa propre copie indépendante de tricks"
            },
            {
              "icon": "",
              "label": "['sit'] — tricks est une liste au niveau de la classe partagée par toutes les instances, donc le changement de d1 est visible sur d2 aussi"
            },
            {
              "icon": "",
              "label": "Un TypeError — les instances ne peuvent pas ajouter à un attribut de classe"
            },
            {
              "icon": "",
              "label": "['sit'] seulement après que d2 = Dog() soit ré-exécuté"
            }
          ]
        },
        {
          "question": "class A a def greet(self): return 'A'. class B(A) a def greet(self): return 'B' + super().greet(). Qu'est-ce que B().greet() retourne ?",
          "options": [
            {
              "icon": "",
              "label": "'AB' — super() s'exécute toujours avant le propre code de la sous-classe"
            },
            {
              "icon": "",
              "label": "'B' — super().greet() est ignoré car B override déjà greet"
            },
            {
              "icon": "",
              "label": "'BA' — super().greet() appelle la méthode de A et son résultat est ajouté à 'B'"
            },
            {
              "icon": "",
              "label": "Un AttributeError, car A n'a pas de méthode greet visible pour B"
            }
          ]
        },
        {
          "question": "class Point stocke self.x dans __init__ et ne définit pas d'autres méthodes. p1 = Point(1) et p2 = Point(1). Qu'est-ce que p1 == p2 retourne par défaut ?",
          "options": [
            {
              "icon": "",
              "label": "True — Python compare tous les attributs correspondants par défaut"
            },
            {
              "icon": "",
              "label": "False — sans un __eq__ personnalisé, == retombe à la comparaison d'identité, et p1 et p2 sont des objets différents"
            },
            {
              "icon": "",
              "label": "True, mais seulement car les deux objets ont été créés avec le même argument"
            },
            {
              "icon": "",
              "label": "Un TypeError, car Point ne définit pas __eq__"
            }
          ]
        },
        {
          "question": "Laquelle des clauses except ci-dessous capturera aussi un ZeroDivisionError : except ArithmeticError, except ValueError, ou except KeyError ?",
          "options": [
            {
              "icon": "",
              "label": "except ValueError — les erreurs de division sont traitées comme des valeurs invalides"
            },
            {
              "icon": "",
              "label": "except KeyError — toute erreur d'exécution non liée à un type spécifique retombe à KeyError"
            },
            {
              "icon": "",
              "label": "Aucun — seul except ZeroDivisionError lui-même peut le capturer"
            },
            {
              "icon": "",
              "label": "except ArithmeticError — ZeroDivisionError est une sous-classe de ArithmeticError"
            }
          ]
        },
        {
          "question": "Vous ouvrez un fichier avec open('data.txt') as f: et une exception est levée lors de la lecture à l'intérieur du bloc. Est-ce que le fichier est toujours fermé ?",
          "options": [
            {
              "icon": "",
              "label": "Non — le fichier reste ouvert car le bloc n'a pas terminé normalement"
            },
            {
              "icon": "",
              "label": "Seulement si vous ajoutez aussi une clause finally autour du statement with"
            },
            {
              "icon": "",
              "label": "Oui — le statement with appelle la méthode __exit__ du fichier, qui le ferme, même si une exception se produit à l'intérieur du bloc"
            },
            {
              "icon": "",
              "label": "Cela dépend du type d'exception — seules les exceptions capturées ailleurs déclenchent le nettoyage"
            }
          ]
        },
        {
          "question": "x = [3, 1, 2] et y = x.sort(). Qu'est-ce que y contient ?",
          "options": [
            {
              "icon": "",
              "label": "[1, 2, 3] — sort() retourne la nouvelle liste triée"
            },
            {
              "icon": "",
              "label": "None — list.sort() trie sur place et retourne None ; x lui-même devient [1, 2, 3]"
            },
            {
              "icon": "",
              "label": "[3, 1, 2] — sort() retourne la liste originale non triée et change seulement l'ordre interne de x"
            },
            {
              "icon": "",
              "label": "Un TypeError, car sort() ne peut pas être assigné à une variable"
            }
          ]
        },
        {
          "question": "Que se passe-t-il quand vous exécutez 'Score: ' + 42 ?",
          "options": [
            {
              "icon": "",
              "label": "'Score: 42' — Python convertit le nombre automatiquement dans une expression +"
            },
            {
              "icon": "",
              "label": "'Score: ' — le nombre est silencieusement supprimé"
            },
            {
              "icon": "",
              "label": "42 — la chaîne est contrainte en nombre et ajoutée"
            },
            {
              "icon": "",
              "label": "TypeError — vous ne pouvez pas concaténer directement un str et un int, 42 doit être converti avec str() en premier"
            }
          ]
        },
        {
          "question": "Vous devez vérifier si une liste appelée results est vide. Quelle est la façon idiomatique de Python : if results == []: , if len(results) == 0: , ou if not results: ?",
          "options": [
            {
              "icon": "",
              "label": "if results == []: — c'est le seul moyen fiable de détecter une liste vide"
            },
            {
              "icon": "",
              "label": "if len(results) == 0: — c'est requis car les listes vides sont truthy en Python"
            },
            {
              "icon": "",
              "label": "Les trois ne sont jamais utilisés en pratique ; l'approche standard est if results is None:"
            },
            {
              "icon": "",
              "label": "if not results: — l'idiome Python s'appuie sur la véracité, et une liste vide est falsy"
            }
          ]
        }
      ],
      "optionOrderVersion": "92945565db6c55da"
    }
  }
}
