{
  "chapter": {
    "id": "dictionnaires",
    "level": "premiere",
    "theme": "Programmation",
    "title": "Dictionnaires",
    "description": "Type construit `dict` en Python : association\nclé-valeur, création, accès, parcours, méthodes\n`keys`, `values` et `items`, test `in`. Comparaison\navec les listes, applications classiques (comptage,\nindexation), implémentation par table de hachage.",
    "parties": [
      {
        "id": "bases",
        "title": "Créer et accéder"
      },
      {
        "id": "parcours",
        "title": "Parcourir et compter"
      },
      {
        "id": "cles",
        "title": "Clés, contraintes et comparaison"
      }
    ],
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Notion de dictionnaire",
      "partie": "bases",
      "statement": "Qu'est-ce qu'un **dictionnaire** Python ?",
      "options": [
        {
          "text": "Un fichier au format CSV chargé en mémoire",
          "correct": false,
          "feedback": "Un fichier CSV est un format de\nstockage de données tabulaires,\nalors qu'un dictionnaire est\nune structure de données interne\nau programme. On peut\nnéanmoins construire un\ndictionnaire à partir d'un\nfichier CSV.\n"
        },
        {
          "text": "Une liste dont les indices sont des entiers consécutifs",
          "correct": false,
          "feedback": "Cette description correspond\nprécisément à une **liste**, et\nnon à un dictionnaire. Une liste\nest indexée par des positions\nentières ; un dictionnaire est\nindexé par des clés\nquelconques.\n"
        },
        {
          "text": "Une structure qui associe des clés à des valeurs",
          "correct": true,
          "feedback": "Bonne réponse : l'accès à une valeur se\nfait directement par sa clé, rapidement.\nOn peut le voir comme un index\nou un annuaire. Un dictionnaire\nest aussi appelé « tableau\nassociatif » dans certains\nlangages.\n"
        },
        {
          "text": "Une chaîne de caractères ordonnée, indexée par position",
          "correct": false,
          "feedback": "Une chaîne de caractères est\nune séquence ordonnée de\ncaractères, alors qu'un\ndictionnaire est une\nstructure associative.\n"
        }
      ],
      "explanation": "Les clés d'un dictionnaire jouent le rôle\ndes indices d'une liste, mais elles peuvent\nêtre des chaînes, des entiers ou des\np-uplets : on retrouve une valeur par sa\nclé, sans connaître sa position."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "creation"
      ],
      "title": "Création",
      "partie": "bases",
      "statement": "Comment crée-t-on un dictionnaire\nvide en Python ?",
      "options": [
        {
          "text": "Avec la syntaxe `d = ()`",
          "correct": false,
          "feedback": "Cette syntaxe crée un **tuple**\nvide, et non un dictionnaire.\n"
        },
        {
          "text": "Avec la syntaxe `d = []`",
          "correct": false,
          "feedback": "Cette syntaxe crée une **liste**\nvide, et non un dictionnaire.\n"
        },
        {
          "text": "Avec la syntaxe `d = {}`",
          "correct": true,
          "feedback": "On utilise des accolades. Une\nforme alternative consiste à\nécrire `d = dict()`.\n"
        },
        {
          "text": "Avec la syntaxe `d = dict[]`",
          "correct": false,
          "feedback": "Cette syntaxe n'est pas valide\nen Python.\n"
        }
      ],
      "explanation": "Pour créer un dictionnaire avec\ndes valeurs initiales :\n`d = {\"alice\": 17, \"bob\": 16}`.\nLes paires sont séparées par des\nvirgules, et chaque paire est\nformée d'une clé et d'une valeur\nséparées par deux-points."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "acces"
      ],
      "title": "Accès par clé",
      "partie": "bases",
      "statement": "Comment accède-t-on à la **valeur**\nassociée à la clé `\"nom\"` dans le\ndictionnaire `d = {\"nom\": \"Alice\"}` ?",
      "options": [
        {
          "text": "Avec `d(nom)`",
          "correct": false,
          "feedback": "Les parenthèses correspondent\nà un appel de fonction, pas à\nun accès dans un\ndictionnaire.\n"
        },
        {
          "text": "Avec `d.nom`",
          "correct": false,
          "feedback": "Cette syntaxe est utilisée pour\naccéder aux attributs d'un\nobjet, et non aux clés d'un\ndictionnaire.\n"
        },
        {
          "text": "Avec `d[\"nom\"]`",
          "correct": true,
          "feedback": "On utilise les crochets, en\nplaçant la clé entre\nguillemets s'il s'agit d'une\nchaîne. Si la clé n'existe\npas, Python lève une\nexception `KeyError`.\n"
        },
        {
          "text": "Avec `d->nom`",
          "correct": false,
          "feedback": "Cette syntaxe est utilisée en\nC ou en PHP, mais pas en\nPython.\n"
        }
      ],
      "explanation": "Pour éviter une `KeyError`, on\nteste d'abord la présence de la\nclé avec `if \"nom\" in d:` avant\nde lire `d[\"nom\"]`. Le test\n`\"age\" in d` vaut `False` si le\ndictionnaire n'a pas de clé\n`\"age\"`."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "ajouter"
      ],
      "title": "Ajouter une entrée",
      "partie": "bases",
      "statement": "Comment ajoute-t-on la paire\n`\"age\": 17` à un dictionnaire `d`\nen Python ?",
      "options": [
        {
          "text": "Avec `d.append(\"age\", 17)`",
          "correct": false,
          "feedback": "La méthode `append` n'existe\npas pour les dictionnaires.\nElle est définie pour les\nlistes.\n"
        },
        {
          "text": "Avec `d.put(\"age\", 17)`",
          "correct": false,
          "feedback": "Cette syntaxe est utilisée en\nJava ou en Go, mais pas en\nPython.\n"
        },
        {
          "text": "Avec `d.add(\"age\": 17)`",
          "correct": false,
          "feedback": "La méthode `add` n'existe pas\npour les dictionnaires ; elle\nappartient aux ensembles, un\nautre type de collection qui\nn'est pas au programme de\npremière.\n"
        },
        {
          "text": "Avec `d[\"age\"] = 17`",
          "correct": true,
          "feedback": "Si la clé n'existe pas, elle\nest créée. Si elle existait\ndéjà, sa valeur est tout\nsimplement mise à jour.\n"
        }
      ],
      "explanation": "La même syntaxe permet à la fois\nla création d'une nouvelle\nentrée et la mise à jour d'une\nentrée existante. On ne fait\npas de distinction syntaxique\nentre l'ajout et la\nmodification."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "longueur"
      ],
      "title": "Longueur d'un dictionnaire",
      "partie": "bases",
      "statement": "Comment connaître le **nombre de paires** clé-valeur d'un\ndictionnaire ?",
      "options": [
        {
          "text": "Avec `d.length`",
          "correct": false,
          "feedback": "L'attribut `length` n'existe\npas pour les dictionnaires\nen Python.\n"
        },
        {
          "text": "Avec `len(d)`",
          "correct": true,
          "feedback": "La fonction native `len`\nfonctionne sur les\ndictionnaires comme sur\nd'autres conteneurs (listes,\nchaînes, ensembles, tuples).\n"
        },
        {
          "text": "Avec `d.size()`",
          "correct": false,
          "feedback": "La méthode `size` n'existe\npas pour les dictionnaires\nen Python.\n"
        },
        {
          "text": "Avec `count(d)`",
          "correct": false,
          "feedback": "La fonction `count` n'est pas\ndéfinie sur les\ndictionnaires.\n"
        }
      ],
      "explanation": "L'égalité `len(d) == 0` revient\nà dire que le dictionnaire est\nvide. Comme pour les listes, le\ntest `if d:` est plus\nidiomatique en Python : il est\nvrai si et seulement si le\ndictionnaire n'est pas vide."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "parcours"
      ],
      "title": "Parcourir un dictionnaire",
      "partie": "parcours",
      "statement": "Que parcourt par défaut la boucle\n`for x in d:`, où `d` est un\ndictionnaire ?",
      "options": [
        {
          "text": "Les paires clé-valeur",
          "correct": false,
          "feedback": "Pour parcourir les paires\nclé-valeur, il faut\nexplicitement écrire\n`for k, v in d.items():`.\n"
        },
        {
          "text": "Les clés du dictionnaire",
          "correct": true,
          "feedback": "Cette boucle est équivalente à\n`for x in d.keys():`. Pour\nparcourir les valeurs, on\nécrit `for v in d.values():`.\nPour parcourir les paires\nclé-valeur, on utilise\n`for k, v in d.items():`.\n"
        },
        {
          "text": "Les valeurs du dictionnaire",
          "correct": false,
          "feedback": "Par défaut, la boucle parcourt\nles **clés** du dictionnaire,\net non ses valeurs. Pour\naccéder aux valeurs, on\nutilise `d.values()`.\n"
        },
        {
          "text": "Les indices du dictionnaire",
          "correct": false,
          "feedback": "Un dictionnaire n'a pas\nd'indices entiers comme une\nliste. Il a uniquement des\nclés, qui peuvent être de\ntypes variés.\n"
        }
      ],
      "explanation": "Un idiome très courant :\n\n```python\nfor cle, valeur in d.items():\n    print(cle, valeur)\n```\n\nCette construction est très\npratique pour parcourir un\ndictionnaire tout en conservant\nl'association entre clé et\nvaleur."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "in-cle"
      ],
      "title": "Tester la présence d'une clé",
      "partie": "parcours",
      "statement": "Comment teste-t-on si la clé\n`\"nom\"` existe dans le\ndictionnaire `d` ?",
      "options": [
        {
          "text": "Avec `d[\"nom\"]`",
          "correct": false,
          "feedback": "Cette syntaxe accède\ndirectement à la valeur\nassociée à la clé. Elle\nprovoque une exception\n`KeyError` si la clé est\nabsente.\n"
        },
        {
          "text": "Avec `d.has(\"nom\")`",
          "correct": false,
          "feedback": "La méthode `has` n'existe pas\nen Python 3. Une méthode\n`has_key` existait en Python\n2, mais elle a été supprimée.\n"
        },
        {
          "text": "Avec `d.contains(\"nom\")`",
          "correct": false,
          "feedback": "La méthode `contains` n'existe\npas pour les dictionnaires en\nPython.\n"
        },
        {
          "text": "Avec `\"nom\" in d`",
          "correct": true,
          "feedback": "L'opérateur `in` testé sur un\ndictionnaire vérifie la\nprésence parmi les **clés**.\nCette opération est en\n$O(1)$ en moyenne.\n"
        }
      ],
      "explanation": "Pour tester la présence parmi\nles **valeurs**, on écrit\n`valeur in d.values()`. Cette\nopération est en moyenne en\n$O(n)$, donc plus coûteuse que\nla recherche d'une clé."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "keys"
      ],
      "title": "Méthode keys",
      "partie": "parcours",
      "statement": "Soit `d = {\"a\": 1, \"b\": 2}`. Que vaut\n`list(d.keys())` ?",
      "options": [
        {
          "text": "`[1, 2]`",
          "correct": false,
          "feedback": "Erreur : ce sont les **valeurs**, que renvoie\n`d.values()`. La méthode `keys` ne fournit que\nles clés.\n"
        },
        {
          "text": "`['a', 'b']`",
          "correct": true,
          "feedback": "Bonne réponse : `d.keys()` fournit les clés du\ndictionnaire, dans l'ordre d'insertion, et\n`list(...)` les range dans une liste. C'est\naussi ce que parcourt `for k in d:`.\n"
        },
        {
          "text": "`[('a', 1), ('b', 2)]`",
          "correct": false,
          "feedback": "Erreur : les couples `(clé, valeur)` sont\nfournis par `d.items()`, pas par `keys`.\n"
        },
        {
          "text": "`['a', 1, 'b', 2]`",
          "correct": false,
          "feedback": "Erreur : clés et valeurs ne sont jamais\nmélangées dans une même liste. `keys` ne\nrenvoie que les clés, `values` que les valeurs.\n"
        }
      ],
      "explanation": "Trois vues sur un dictionnaire : `keys()` (les\nclés), `values()` (les valeurs) et `items()` (les\ncouples). Parcourir directement `d` revient à\nparcourir `d.keys()`."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "exemple"
      ],
      "title": "Cas d'usage typique",
      "partie": "bases",
      "statement": "Lequel des cas suivants est un\nusage **typique** d'un\ndictionnaire ?",
      "options": [
        {
          "text": "Associer un identifiant à des informations, comme un nom à un âge",
          "correct": true,
          "feedback": "C'est précisément le rôle\nd'un dictionnaire. Il offre\nune recherche rapide à\npartir de la clé.\n"
        },
        {
          "text": "Compresser efficacement de grandes quantités de données",
          "correct": false,
          "feedback": "La compression de données\nest une fonctionnalité\nspécifique, sans lien\ndirect avec la structure de\ndictionnaire.\n"
        },
        {
          "text": "Stocker une séquence ordonnée de notes d'élèves",
          "correct": false,
          "feedback": "Pour ce cas, une liste est\nplus adaptée. Le\ndictionnaire excelle\nplutôt pour l'association\nentre une clé et une\nvaleur.\n"
        },
        {
          "text": "Trier une liste de valeurs",
          "correct": false,
          "feedback": "Le tri d'une liste est une\nopération qui n'a rien à voir\navec les dictionnaires.\n"
        }
      ],
      "explanation": "Quelques cas d'usage classiques :\nindexation (mot vers\ndéfinition), comptage (mot vers\nnombre d'occurrences),\nconfiguration (option vers\nvaleur)."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "acces",
        "cle-variable"
      ],
      "title": "Clé fournie par une variable",
      "partie": "bases",
      "statement": "Que produit ce code ?\n```python\nnom = \"Alice\"\nd = {\"Alice\": 17}\nprint(d[nom])\n```",
      "options": [
        {
          "text": "Il lève une `KeyError`, car `nom` n'est pas une clé de `d`",
          "correct": false,
          "feedback": "Erreur : entre les crochets, Python évalue\nd'abord l'expression `nom`, qui vaut\n`\"Alice\"`. C'est cette chaîne qui sert de clé,\net elle est bien présente.\n"
        },
        {
          "text": "Il affiche `17`",
          "correct": true,
          "feedback": "Bonne réponse : `d[nom]` équivaut à\n`d[\"Alice\"]`, puisque `nom` contient la chaîne\n`\"Alice\"`. La valeur associée, $17$, est\naffichée.\n"
        },
        {
          "text": "Il affiche `Alice`",
          "correct": false,
          "feedback": "Erreur : l'accès par clé renvoie la **valeur**\nassociée, pas la clé elle-même. `Alice` n'est\nque le contenu de la variable `nom`.\n"
        },
        {
          "text": "Il lève une `KeyError`, car il manque les guillemets autour de `nom`",
          "correct": false,
          "feedback": "Erreur : avec des guillemets, `d[\"nom\"]`\nchercherait la clé `\"nom\"`, absente, et c'est\nlà que `KeyError` surviendrait. Sans\nguillemets, `nom` est une variable dont la\nvaleur `\"Alice\"` sert de clé.\n"
        }
      ],
      "explanation": "La clé entre crochets est une expression quelconque :\nun littéral (`d[\"Alice\"]`), une variable (`d[nom]`)\nou un calcul. Distinguer `d[nom]` (valeur de la\nvariable) de `d[\"nom\"]` (chaîne littérale) évite\nbien des `KeyError`.",
      "variants": [
        {
          "statement": "Que produit ce code ?\n```python\nnom = \"Alice\"\nd = {\"Alice\": 17}\nprint(d[nom])\n```",
          "options": [
            {
              "text": "Il lève une `KeyError`, car `nom` n'est pas une clé de `d`",
              "correct": false,
              "feedback": "Erreur : entre les crochets, Python évalue\nd'abord l'expression `nom`, qui vaut\n`\"Alice\"`. C'est cette chaîne qui sert de clé,\net elle est bien présente.\n"
            },
            {
              "text": "Il affiche `17`",
              "correct": true,
              "feedback": "Bonne réponse : `d[nom]` équivaut à\n`d[\"Alice\"]`, puisque `nom` contient la chaîne\n`\"Alice\"`. La valeur associée, $17$, est\naffichée.\n"
            },
            {
              "text": "Il affiche `Alice`",
              "correct": false,
              "feedback": "Erreur : l'accès par clé renvoie la **valeur**\nassociée, pas la clé elle-même. `Alice` n'est\nque le contenu de la variable `nom`.\n"
            },
            {
              "text": "Il lève une `KeyError`, car il manque les guillemets autour de `nom`",
              "correct": false,
              "feedback": "Erreur : avec des guillemets, `d[\"nom\"]`\nchercherait la clé `\"nom\"`, absente, et c'est\nlà que `KeyError` surviendrait. Sans\nguillemets, `nom` est une variable dont la\nvaleur `\"Alice\"` sert de clé.\n"
            }
          ],
          "explanation": "La clé entre crochets est une expression quelconque :\nun littéral (`d[\"Alice\"]`), une variable (`d[nom]`)\nou un calcul. Distinguer `d[nom]` (valeur de la\nvariable) de `d[\"nom\"]` (chaîne littérale) évite\nbien des `KeyError`."
        },
        {
          "statement": "Que produit ce code ?\n```python\nnom = \"Bruno\"\nd = {\"Bruno\": 42}\nprint(d[nom])\n```",
          "options": [
            {
              "text": "Il lève une `KeyError`, car `nom` n'est pas une clé de `d`",
              "correct": false,
              "feedback": "Erreur : entre les crochets, Python évalue\nd'abord l'expression `nom`, qui vaut\n`\"Bruno\"`. C'est cette chaîne qui sert de clé,\net elle est bien présente.\n"
            },
            {
              "text": "Il affiche `42`",
              "correct": true,
              "feedback": "Bonne réponse : `d[nom]` équivaut à\n`d[\"Bruno\"]`, puisque `nom` contient la chaîne\n`\"Bruno\"`. La valeur associée, $42$, est\naffichée.\n"
            },
            {
              "text": "Il affiche `Bruno`",
              "correct": false,
              "feedback": "Erreur : l'accès par clé renvoie la **valeur**\nassociée, pas la clé elle-même. `Bruno` n'est\nque le contenu de la variable `nom`.\n"
            },
            {
              "text": "Il lève une `KeyError`, car il manque les guillemets autour de `nom`",
              "correct": false,
              "feedback": "Erreur : avec des guillemets, `d[\"nom\"]`\nchercherait la clé `\"nom\"`, absente, et c'est\nlà que `KeyError` surviendrait. Sans\nguillemets, `nom` est une variable dont la\nvaleur `\"Bruno\"` sert de clé.\n"
            }
          ],
          "explanation": "La clé entre crochets est une expression quelconque :\nun littéral (`d[\"Bruno\"]`), une variable (`d[nom]`)\nou un calcul. Distinguer `d[nom]` (valeur de la\nvariable) de `d[\"nom\"]` (chaîne littérale) évite\nbien des `KeyError`."
        },
        {
          "statement": "Que produit ce code ?\n```python\nnom = \"Chloé\"\nd = {\"Chloé\": 8}\nprint(d[nom])\n```",
          "options": [
            {
              "text": "Il lève une `KeyError`, car `nom` n'est pas une clé de `d`",
              "correct": false,
              "feedback": "Erreur : entre les crochets, Python évalue\nd'abord l'expression `nom`, qui vaut\n`\"Chloé\"`. C'est cette chaîne qui sert de clé,\net elle est bien présente.\n"
            },
            {
              "text": "Il affiche `8`",
              "correct": true,
              "feedback": "Bonne réponse : `d[nom]` équivaut à\n`d[\"Chloé\"]`, puisque `nom` contient la chaîne\n`\"Chloé\"`. La valeur associée, $8$, est\naffichée.\n"
            },
            {
              "text": "Il affiche `Chloé`",
              "correct": false,
              "feedback": "Erreur : l'accès par clé renvoie la **valeur**\nassociée, pas la clé elle-même. `Chloé` n'est\nque le contenu de la variable `nom`.\n"
            },
            {
              "text": "Il lève une `KeyError`, car il manque les guillemets autour de `nom`",
              "correct": false,
              "feedback": "Erreur : avec des guillemets, `d[\"nom\"]`\nchercherait la clé `\"nom\"`, absente, et c'est\nlà que `KeyError` surviendrait. Sans\nguillemets, `nom` est une variable dont la\nvaleur `\"Chloé\"` sert de clé.\n"
            }
          ],
          "explanation": "La clé entre crochets est une expression quelconque :\nun littéral (`d[\"Chloé\"]`), une variable (`d[nom]`)\nou un calcul. Distinguer `d[nom]` (valeur de la\nvariable) de `d[\"nom\"]` (chaîne littérale) évite\nbien des `KeyError`."
        }
      ]
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "comptage"
      ],
      "title": "Compter les occurrences",
      "partie": "parcours",
      "statement": "Quel code **compte** le nombre\nd'occurrences de chaque mot dans\nune liste `mots` ?",
      "options": [
        {
          "text": "```python\nc = {}\nfor m in mots:\n    if m in c:\n        c[m] = c[m] + 1\n    else:\n        c[m] = 1\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : c'est le motif classique du\ncomptage. Le test `m in c` distingue la\npremière rencontre d'un mot (compteur créé\nà $1$) des suivantes (compteur incrémenté).\n"
        },
        {
          "text": "```python\nc = {}\nfor m in mots:\n    c[m] = c[m] + 1\n```\n",
          "correct": false,
          "feedback": "Erreur : `c[m] + 1` lit `c[m]` avant d'écrire.\nÀ la première occurrence d'un mot, la clé\nn'existe pas encore et Python lève `KeyError`.\nIl manque le test `if m in c`.\n"
        },
        {
          "text": "```python\nc = {}\nfor m in mots:\n    if m in c:\n        c[m] = c[m] + 1\n    else:\n        c[m] = 0\n```\n",
          "correct": false,
          "feedback": "Erreur : la première occurrence est comptée\npour $0$ au lieu de $1$. Chaque mot finit avec\nun compte inférieur d'une unité au vrai nombre\nd'occurrences : sur `[\"a\", \"b\", \"a\"]`, on\nobtient `{\"a\": 1, \"b\": 0}` au lieu de\n`{\"a\": 2, \"b\": 1}`.\n"
        },
        {
          "text": "```python\nc = {}\nfor m in mots:\n    c[m] = 1\n```\n",
          "correct": false,
          "feedback": "Erreur : chaque occurrence remet le compteur à\n$1$ au lieu de l'incrémenter. À la fin, tous\nles mots ont pour valeur $1$.\n"
        }
      ],
      "explanation": "Ce motif « tester puis créer ou incrémenter »\ns'applique à tout comptage avec un dictionnaire :\nla clé est l'objet compté, la valeur le nombre de\nrencontres. Le test `in` évite la `KeyError`\nqu'entraînerait la lecture d'une clé absente."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "valeur-liste",
        "mutabilite"
      ],
      "title": "Une valeur qui est une liste",
      "partie": "bases",
      "statement": "Que produit ce code ?\n\n```python\nd = {\"fruits\": [\"pomme\"]}\nd[\"fruits\"].append(\"poire\")\nprint(d)\n```",
      "options": [
        {
          "text": "Il affiche `{'fruits': ['pomme', 'poire']}`",
          "correct": true,
          "feedback": "Bonne réponse : `d[\"fruits\"]` renvoie la liste\nrangée dans le dictionnaire, pas une copie.\nL'appel `append` allonge donc cette liste, et\nle changement est visible dans `d`.\n"
        },
        {
          "text": "Il affiche `{'fruits': ['pomme']}`",
          "correct": false,
          "feedback": "Erreur : l'accès `d[\"fruits\"]` ne copie pas la\nliste. `append` agit sur l'objet même qui est\nstocké dans le dictionnaire.\n"
        },
        {
          "text": "Il affiche `{'fruits': ['poire']}`",
          "correct": false,
          "feedback": "Erreur : `append` ajoute un élément à la fin de\nla liste sans effacer les autres. Seule une\naffectation `d[\"fruits\"] = [\"poire\"]`\nremplacerait la valeur.\n"
        },
        {
          "text": "Il lève une `AttributeError`, car un dictionnaire n'a pas de méthode `append`",
          "correct": false,
          "feedback": "Erreur : la méthode n'est pas appelée sur `d`\nmais sur `d[\"fruits\"]`, qui est une liste. Une\nliste possède bien la méthode `append`.\n"
        }
      ],
      "explanation": "Une valeur de dictionnaire peut être une liste, un\ntuple ou un autre dictionnaire. Comme\n`d[\"fruits\"]` renvoie l'objet lui-même, les\nméthodes qui modifient une liste agissent\ndirectement sur la valeur stockée."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "items"
      ],
      "title": "Méthode items",
      "partie": "parcours",
      "statement": "Que renvoie l'appel `d.items()` ?",
      "options": [
        {
          "text": "Une liste contenant uniquement l'ensemble des clés",
          "correct": false,
          "feedback": "Cette description correspond\nplutôt à `d.keys()`. La\nméthode `items` renvoie\nautre chose.\n"
        },
        {
          "text": "Un ensemble contenant les valeurs",
          "correct": false,
          "feedback": "Pour obtenir les valeurs,\non utilise plutôt\n`d.values()`. La méthode\n`items` renvoie les paires\ncomplètes.\n"
        },
        {
          "text": "La taille du dictionnaire",
          "correct": false,
          "feedback": "La taille du dictionnaire\ns'obtient avec la fonction\n`len(d)`.\n"
        },
        {
          "text": "Une vue itérable sur les paires `(clé, valeur)`",
          "correct": true,
          "feedback": "Cette vue permet d'itérer\nsur les deux à la fois,\navec la syntaxe\n`for k, v in d.items():`.\n"
        }
      ],
      "explanation": "Trois vues complémentaires sont\ndisponibles : `keys()` pour les\nclés, `values()` pour les\nvaleurs, et `items()` pour les\npaires. Elles sont\ndynamiques : elles reflètent\nles modifications du\ndictionnaire au fur et à\nmesure."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "hashable-cles"
      ],
      "title": "Clés autorisées dans un dictionnaire",
      "partie": "cles",
      "statement": "Lequel des types suivants ne peut\n**pas** être utilisé comme clé de\ndictionnaire ?",
      "options": [
        {
          "text": "Le type `str` (chaîne)",
          "correct": false,
          "feedback": "Les chaînes sont sans\ndoute le type de clé le\nplus utilisé dans la\npratique.\n"
        },
        {
          "text": "Le type `tuple`, à condition que tous ses éléments soient eux-mêmes utilisables comme clés",
          "correct": false,
          "feedback": "Un tuple est autorisé\ncomme clé tant que tous\nses éléments sont eux-mêmes\nimmuables.\n"
        },
        {
          "text": "Le type `list` (liste)",
          "correct": true,
          "feedback": "Les listes sont **mutables**,\nce qui les empêche d'être\nutilisées comme clés.\nTenter d'écrire\n`d[[1, 2]] = 5` provoque\nune `TypeError`.\n"
        },
        {
          "text": "Le type `int` (entier)",
          "correct": false,
          "feedback": "Les entiers sont\nparfaitement utilisables\ncomme clés de\ndictionnaire.\n"
        }
      ],
      "explanation": "La règle est la suivante : la\nclé doit être **immuable**.\nUn tuple composé d'éléments\nimmuables est donc autorisé,\nmais un tuple contenant une\nliste ne l'est pas."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "vs-liste"
      ],
      "title": "Dictionnaire et liste",
      "partie": "cles",
      "statement": "Pour rechercher une valeur à\npartir d'un identifiant (par\nexemple « l'élève dont\nl'identifiant est $42$ »),\npourquoi préfère-t-on un\ndictionnaire à une liste ?",
      "options": [
        {
          "text": "Pour la rapidité de l'accès par clé",
          "correct": true,
          "feedback": "Bonne réponse : on accède directement à la\nvaleur par sa clé, alors qu'avec une liste il\nfaut parcourir les éléments un à un jusqu'à\ntrouver l'identifiant, avec un coût linéaire.\nSur de gros volumes de données, la\ndifférence est considérable.\n"
        },
        {
          "text": "Parce qu'une liste ne peut pas contenir d'identifiants numériques",
          "correct": false,
          "feedback": "Cette affirmation est\nfausse. Les listes\npeuvent contenir tout\ntype de valeurs,\nnotamment des nombres.\n"
        },
        {
          "text": "Pour économiser une part importante de la mémoire",
          "correct": false,
          "feedback": "Au contraire, un\ndictionnaire occupe en\ngénéral plus de mémoire\nqu'une liste équivalente.\n"
        },
        {
          "text": "Aucune raison, les deux structures se valent pour cet usage",
          "correct": false,
          "feedback": "Erreur : il existe une différence majeure\nde performance. Avec une liste, il faut\nparcourir les éléments un à un jusqu'à\ntrouver l'identifiant.\n"
        }
      ],
      "explanation": "Compromis : le dictionnaire\nconsomme plus de mémoire,\nmais offre une recherche\ntrès rapide par clé. La liste,\nplus compacte, doit être\nparcourue pour retrouver un\nidentifiant."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "exemple-traduction"
      ],
      "title": "Représenter une table de traduction",
      "partie": "bases",
      "statement": "Comment représenter une **table de traduction** simple du\nfrançais vers l'anglais en\nPython ?",
      "options": [
        {
          "text": "```python\n{\"chat\": \"cat\", \"chien\": \"dog\"}\n```\n",
          "correct": true,
          "feedback": "On utilise un dictionnaire,\nce qui permet un accès\nimmédiat :\n`traduction[\"chat\"]`\nrenvoie `\"cat\"`.\n"
        },
        {
          "text": "```python\n(\"chat\", \"cat\")\n```\n",
          "correct": false,
          "feedback": "Ce tuple ne contient\nqu'un seul couple. Il ne\npermet pas de stocker\nl'ensemble des\ntraductions.\n"
        },
        {
          "text": "```python\n[\"chat\", \"cat\", \"chien\", \"dog\"]\n```\n",
          "correct": false,
          "feedback": "Cette structure est une\nliste plate qui ne\npermet pas une recherche\nrapide par mot\nfrançais. Il faudrait\nparcourir la liste pour\nretrouver la\ntraduction.\n"
        },
        {
          "text": "```python\nchat = \"cat\"; chien = \"dog\"\n```\n",
          "correct": false,
          "feedback": "Définir une variable par\nmot est très peu\npratique : la solution\nne passe pas à\nl'échelle dès que le\nnombre de mots\naugmente.\n"
        }
      ],
      "explanation": "Pour une **petite** table de\ntraduction, un dictionnaire\nsimple convient. Pour des\nbases volumineuses, on a\nplutôt recours à une base\nde données ou à des\nfichiers indexés."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "construction",
        "listes-paralleles"
      ],
      "title": "Construction depuis deux listes",
      "partie": "bases",
      "statement": "```python\nnoms = [\"Lou\", \"Max\"]\nages = [16, 17]\nd = {}\nfor i in range(len(noms)):\n    d[noms[i]] = ages[i]\nprint(d)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `{'Lou': 16, 'Max': 17}`",
          "correct": true,
          "feedback": "Bonne réponse : à chaque tour, `noms[i]` fournit\nla clé et `ages[i]` la valeur de même rang.\nL'affectation `d[cle] = valeur` crée l'entrée\nsi la clé est absente.\n"
        },
        {
          "text": "Il affiche `{16: 'Lou', 17: 'Max'}`",
          "correct": false,
          "feedback": "Erreur : dans `d[noms[i]] = ages[i]`,\nl'expression entre crochets est la **clé**.\nCe sont donc les noms qui servent de clés et\nles âges qui sont les valeurs.\n"
        },
        {
          "text": "Il affiche `{0: 'Lou', 1: 'Max'}`",
          "correct": false,
          "feedback": "Erreur : l'indice `i` ne sert qu'à lire les deux\nlistes au même rang ; il n'est jamais utilisé\ncomme clé. La clé est `noms[i]`, une chaîne.\n"
        },
        {
          "text": "Il lève une `KeyError`, car les clés `'Lou'` et `'Max'` n'existent pas encore",
          "correct": false,
          "feedback": "Erreur : `KeyError` ne concerne que la\n**lecture** d'une clé absente. Ici on écrit\ndans `d`, et l'affectation crée la clé si elle\nn'existe pas.\n"
        }
      ],
      "explanation": "Le parcours par indice `range(len(noms))` permet\nd'apparier deux listes de même longueur, et\nl'affectation `d[cle] = valeur` remplit le\ndictionnaire entrée par entrée. C'est une façon\ncourante de construire un dictionnaire à partir de\ndonnées tabulaires.",
      "variants": [
        {
          "statement": "```python\nnoms = [\"Lou\", \"Max\"]\nages = [16, 17]\nd = {}\nfor i in range(len(noms)):\n    d[noms[i]] = ages[i]\nprint(d)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `{'Lou': 16, 'Max': 17}`",
              "correct": true,
              "feedback": "Bonne réponse : à chaque tour, `noms[i]` fournit\nla clé et `ages[i]` la valeur de même rang.\nL'affectation `d[cle] = valeur` crée l'entrée\nsi la clé est absente.\n"
            },
            {
              "text": "Il affiche `{16: 'Lou', 17: 'Max'}`",
              "correct": false,
              "feedback": "Erreur : dans `d[noms[i]] = ages[i]`,\nl'expression entre crochets est la **clé**.\nCe sont donc les noms qui servent de clés et\nles âges qui sont les valeurs.\n"
            },
            {
              "text": "Il affiche `{0: 'Lou', 1: 'Max'}`",
              "correct": false,
              "feedback": "Erreur : l'indice `i` ne sert qu'à lire les deux\nlistes au même rang ; il n'est jamais utilisé\ncomme clé. La clé est `noms[i]`, une chaîne.\n"
            },
            {
              "text": "Il lève une `KeyError`, car les clés `'Lou'` et `'Max'` n'existent pas encore",
              "correct": false,
              "feedback": "Erreur : `KeyError` ne concerne que la\n**lecture** d'une clé absente. Ici on écrit\ndans `d`, et l'affectation crée la clé si elle\nn'existe pas.\n"
            }
          ],
          "explanation": "Le parcours par indice `range(len(noms))` permet\nd'apparier deux listes de même longueur, et\nl'affectation `d[cle] = valeur` remplit le\ndictionnaire entrée par entrée. C'est une façon\ncourante de construire un dictionnaire à partir de\ndonnées tabulaires."
        },
        {
          "statement": "```python\nnoms = [\"Ava\", \"Tom\"]\nages = [15, 18]\nd = {}\nfor i in range(len(noms)):\n    d[noms[i]] = ages[i]\nprint(d)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `{'Ava': 15, 'Tom': 18}`",
              "correct": true,
              "feedback": "Bonne réponse : à chaque tour, `noms[i]` fournit\nla clé et `ages[i]` la valeur de même rang.\nL'affectation `d[cle] = valeur` crée l'entrée\nsi la clé est absente.\n"
            },
            {
              "text": "Il affiche `{15: 'Ava', 18: 'Tom'}`",
              "correct": false,
              "feedback": "Erreur : dans `d[noms[i]] = ages[i]`,\nl'expression entre crochets est la **clé**.\nCe sont donc les noms qui servent de clés et\nles âges qui sont les valeurs.\n"
            },
            {
              "text": "Il affiche `{0: 'Ava', 1: 'Tom'}`",
              "correct": false,
              "feedback": "Erreur : l'indice `i` ne sert qu'à lire les deux\nlistes au même rang ; il n'est jamais utilisé\ncomme clé. La clé est `noms[i]`, une chaîne.\n"
            },
            {
              "text": "Il lève une `KeyError`, car les clés `'Ava'` et `'Tom'` n'existent pas encore",
              "correct": false,
              "feedback": "Erreur : `KeyError` ne concerne que la\n**lecture** d'une clé absente. Ici on écrit\ndans `d`, et l'affectation crée la clé si elle\nn'existe pas.\n"
            }
          ],
          "explanation": "Le parcours par indice `range(len(noms))` permet\nd'apparier deux listes de même longueur, et\nl'affectation `d[cle] = valeur` remplit le\ndictionnaire entrée par entrée. C'est une façon\ncourante de construire un dictionnaire à partir de\ndonnées tabulaires."
        },
        {
          "statement": "```python\nnoms = [\"Nino\", \"Sara\"]\nages = [17, 16]\nd = {}\nfor i in range(len(noms)):\n    d[noms[i]] = ages[i]\nprint(d)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `{'Nino': 17, 'Sara': 16}`",
              "correct": true,
              "feedback": "Bonne réponse : à chaque tour, `noms[i]` fournit\nla clé et `ages[i]` la valeur de même rang.\nL'affectation `d[cle] = valeur` crée l'entrée\nsi la clé est absente.\n"
            },
            {
              "text": "Il affiche `{17: 'Nino', 16: 'Sara'}`",
              "correct": false,
              "feedback": "Erreur : dans `d[noms[i]] = ages[i]`,\nl'expression entre crochets est la **clé**.\nCe sont donc les noms qui servent de clés et\nles âges qui sont les valeurs.\n"
            },
            {
              "text": "Il affiche `{0: 'Nino', 1: 'Sara'}`",
              "correct": false,
              "feedback": "Erreur : l'indice `i` ne sert qu'à lire les deux\nlistes au même rang ; il n'est jamais utilisé\ncomme clé. La clé est `noms[i]`, une chaîne.\n"
            },
            {
              "text": "Il lève une `KeyError`, car les clés `'Nino'` et `'Sara'` n'existent pas encore",
              "correct": false,
              "feedback": "Erreur : `KeyError` ne concerne que la\n**lecture** d'une clé absente. Ici on écrit\ndans `d`, et l'affectation crée la clé si elle\nn'existe pas.\n"
            }
          ],
          "explanation": "Le parcours par indice `range(len(noms))` permet\nd'apparier deux listes de même longueur, et\nl'affectation `d[cle] = valeur` remplit le\ndictionnaire entrée par entrée. C'est une façon\ncourante de construire un dictionnaire à partir de\ndonnées tabulaires."
        }
      ]
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "parcours-cles",
        "tri"
      ],
      "title": "Trier des fruits selon leur stock",
      "partie": "parcours",
      "statement": "Un épicier veut lister ses fruits du moins au plus\napprovisionné :\n\n```python\nstock = {\"pommes\": 12, \"poires\": 5, \"kiwis\": 8}\n\ndef quantite(fruit):\n    return stock[fruit]\n\nprint(sorted(stock, key=quantite))\n```\n\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`['poires', 'kiwis', 'pommes']`",
          "correct": true,
          "feedback": "Bonne réponse : `sorted(stock)` trie les clés, et\n`key=quantite` les compare selon la valeur\nassociée : $5$, puis $8$, puis $12$.\n"
        },
        {
          "text": "`[5, 8, 12]`",
          "correct": false,
          "feedback": "Erreur : parcourir un dictionnaire, c'est parcourir\nses clés. `sorted` renvoie donc des noms de fruits ;\nles quantités servent seulement à les comparer.\n"
        },
        {
          "text": "`['kiwis', 'poires', 'pommes']`",
          "correct": false,
          "feedback": "Erreur : c'est l'ordre alphabétique, obtenu avec\n`sorted(stock)` sans fonction clé. Ici, `quantite`\nimpose de comparer les stocks.\n"
        },
        {
          "text": "`[('poires', 5), ('kiwis', 8), ('pommes', 12)]`",
          "correct": false,
          "feedback": "Erreur : ce seraient les couples de `stock.items()`.\n`sorted(stock, key=quantite)` ne trie que les clés et\nrenvoie une liste de noms.\n"
        }
      ],
      "explanation": "`sorted` appliqué à un dictionnaire trie ses clés. Avec une\nfonction clé comme `quantite`, qui renvoie la valeur associée\nà chaque clé, on obtient les clés rangées selon leurs valeurs."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "vide",
        "longueur"
      ],
      "title": "Reconnaître un dictionnaire vide",
      "partie": "bases",
      "statement": "Le dictionnaire `inscrits` associe à chaque atelier la liste de\nses participants. Quelle condition vaut `True` exactement\nlorsque `inscrits` ne contient aucune entrée ?",
      "options": [
        {
          "text": "`len(inscrits) == 0`",
          "correct": true,
          "feedback": "Bonne réponse : `len` renvoie le nombre d'entrées\ndu dictionnaire ; il vaut $0$ exactement quand le\ndictionnaire est vide.\n"
        },
        {
          "text": "`inscrits == []`",
          "correct": false,
          "feedback": "Erreur : un dictionnaire n'est jamais égal à une\nliste, même vide. Cette condition vaut toujours\n`False`, y compris pour `{}`.\n"
        },
        {
          "text": "`inscrits[0] == 0`",
          "correct": false,
          "feedback": "Erreur : `inscrits[0]` cherche la clé `0`. Sur un\ndictionnaire vide, cette clé n'existe pas et Python\nlève `KeyError`.\n"
        },
        {
          "text": "`inscrits.values() == 0`",
          "correct": false,
          "feedback": "Erreur : `values()` renvoie les valeurs du\ndictionnaire, pas leur nombre ; la comparaison avec\n$0$ vaut toujours `False`.\n"
        }
      ],
      "explanation": "Le nombre d'entrées d'un dictionnaire s'obtient avec `len`,\ncomme pour une liste : `len({})` vaut $0$. Comparer un\ndictionnaire à une liste ou lire une clé absente ne permet\npas de tester qu'il est vide."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "piege-modification"
      ],
      "title": "Modifier un dictionnaire pendant son parcours",
      "partie": "bases",
      "statement": "Pourquoi est-il dangereux de\nmodifier un dictionnaire\npendant qu'on l'**itère** ?",
      "options": [
        {
          "text": "Parce que les modifications sont perdues à la fin du parcours",
          "correct": false,
          "feedback": "Erreur : les modifications ne sont pas\nperdues. Le danger est que Python lève\n`RuntimeError` si la taille change, ou que\nle parcours devienne imprévisible.\n"
        },
        {
          "text": "Parce que le dictionnaire est copié à chaque tour de boucle",
          "correct": false,
          "feedback": "Erreur : aucune copie n'est faite. Le\nparcours travaille sur le dictionnaire\nlui-même, ce qui est justement le\nproblème.\n"
        },
        {
          "text": "Aucun danger, Python applique les modifications au tour suivant",
          "correct": false,
          "feedback": "Erreur : Python détecte le changement de\ntaille et lève `RuntimeError` au lieu de\ncontinuer.\n"
        },
        {
          "text": "Parce que le parcours peut échouer ou devenir imprévisible",
          "correct": true,
          "feedback": "Bonne réponse : ajouter ou supprimer une\nclé change la taille du dictionnaire ;\nPython le détecte et lève `RuntimeError`\n(« dictionary changed size during\niteration »). Pour modifier\nen sécurité, il faut\nitérer sur une **copie**\nde la liste des clés,\npar exemple\n`for k in list(d):`.\n"
        }
      ],
      "explanation": "Règle générale : ne\n**jamais** modifier une\ncollection (dictionnaire,\nensemble, liste, etc.)\npendant son parcours. Il\nvaut mieux travailler sur\nune copie ou collecter les\nmodifications dans une\nstructure auxiliaire."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "partie": "cles",
      "statement": "Parmi les affirmations\nsuivantes sur les\ndictionnaires, laquelle est\n**fausse** ?",
      "options": [
        {
          "text": "L'opérateur `in` teste la présence d'une clé, pas celle d'une valeur",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte. Sur un\ndictionnaire, `k in d`\ncherche `k` parmi les\nclés ; pour les valeurs,\nil faut écrire\n`v in d.values()`.\n"
        },
        {
          "text": "Affecter `d[k] = v` avec une clé `k` déjà présente remplace l'ancienne valeur",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte. Une clé est\nunique : une nouvelle\naffectation écrase\nl'ancienne valeur, sans\ncréer de doublon.\n"
        },
        {
          "text": "Une liste peut être utilisée comme clé d'un dictionnaire",
          "correct": true,
          "feedback": "Cette affirmation est\nfausse (donc c'est la\nbonne réponse). Les\nlistes étant modifiables,\nelles ne peuvent pas\nservir de clés. Les\nchaînes, les entiers et\nles p-uplets d'éléments\nimmuables le peuvent.\n"
        },
        {
          "text": "La méthode `items()` permet de parcourir les couples (clé, valeur)",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte : la boucle\n`for k, v in d.items():`\nparcourt chaque clé avec\nsa valeur.\n"
        }
      ],
      "explanation": "Mémo utile : la clé d'un\ndictionnaire doit être\nstable dans le temps. Tout\nce qui est mutable n'est\ndonc pas autorisé comme\nclé."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "values",
        "somme"
      ],
      "title": "Méthode values",
      "partie": "parcours",
      "statement": "On dispose du dictionnaire suivant :\n```python\nstock = {\"stylo\": 5, \"cahier\": 12, \"gomme\": 3}\n```\nQue renvoie l'expression `sum(stock.values())` ?",
      "options": [
        {
          "text": "La somme des longueurs des clés (16)",
          "correct": false,
          "feedback": "Erreur : `values` agit sur les valeurs, pas sur\nles clés. La longueur des chaînes utilisées\ncomme clés n'intervient pas ici.\n"
        },
        {
          "text": "Une erreur de type",
          "correct": false,
          "feedback": "Erreur : la fonction `sum` accepte parfaitement\nun itérable de nombres. La méthode `values`\nrenvoie justement un itérable, donc\nl'opération est valide.\n"
        },
        {
          "text": "$20$",
          "correct": true,
          "feedback": "Bonne réponse : `stock.values()` renvoie une vue\nsur les valeurs `[5, 12, 3]`, et `sum` calcule\nleur total : $5 + 12 + 3 = 20$.\n"
        },
        {
          "text": "$3$",
          "correct": false,
          "feedback": "Erreur : ce serait le nombre de **clés**, soit\nla longueur du dictionnaire (`len(stock)`).\nLa somme demandée porte sur les **valeurs**, pas\nsur le nombre d'entrées.\n"
        }
      ],
      "explanation": "Trois vues complémentaires sur un dictionnaire :\n`d.keys()` (les clés), `d.values()` (les valeurs)\net `d.items()` (les couples). Combinées avec les\nfonctions natives `sum`, `max`, `min` ou `len`,\nelles permettent d'écrire des traitements concis.",
      "variants": [
        {
          "statement": "On dispose du dictionnaire suivant :\n```python\nstock = {\"stylo\": 5, \"cahier\": 12, \"gomme\": 3}\n```\nQue renvoie l'expression `sum(stock.values())` ?",
          "options": [
            {
              "text": "La somme des longueurs des clés (16)",
              "correct": false,
              "feedback": "Erreur : `values` agit sur les valeurs, pas sur\nles clés. La longueur des chaînes utilisées\ncomme clés n'intervient pas ici.\n"
            },
            {
              "text": "Une erreur de type",
              "correct": false,
              "feedback": "Erreur : la fonction `sum` accepte parfaitement\nun itérable de nombres. La méthode `values`\nrenvoie justement un itérable, donc\nl'opération est valide.\n"
            },
            {
              "text": "$20$",
              "correct": true,
              "feedback": "Bonne réponse : `stock.values()` renvoie une vue\nsur les valeurs `[5, 12, 3]`, et `sum` calcule\nleur total : $5 + 12 + 3 = 20$.\n"
            },
            {
              "text": "$3$",
              "correct": false,
              "feedback": "Erreur : ce serait le nombre de **clés**, soit\nla longueur du dictionnaire (`len(stock)`).\nLa somme demandée porte sur les **valeurs**, pas\nsur le nombre d'entrées.\n"
            }
          ],
          "explanation": "Trois vues complémentaires sur un dictionnaire :\n`d.keys()` (les clés), `d.values()` (les valeurs)\net `d.items()` (les couples). Combinées avec les\nfonctions natives `sum`, `max`, `min` ou `len`,\nelles permettent d'écrire des traitements concis."
        },
        {
          "statement": "On dispose du dictionnaire suivant :\n```python\nstock = {\"stylo\": 8, \"cahier\": 4, \"gomme\": 9}\n```\nQue renvoie l'expression `sum(stock.values())` ?",
          "options": [
            {
              "text": "La somme des longueurs des clés (16)",
              "correct": false,
              "feedback": "Erreur : `values` agit sur les valeurs, pas sur\nles clés. La longueur des chaînes utilisées\ncomme clés n'intervient pas ici.\n"
            },
            {
              "text": "Une erreur de type",
              "correct": false,
              "feedback": "Erreur : la fonction `sum` accepte parfaitement\nun itérable de nombres. La méthode `values`\nrenvoie justement un itérable, donc\nl'opération est valide.\n"
            },
            {
              "text": "$21$",
              "correct": true,
              "feedback": "Bonne réponse : `stock.values()` renvoie une vue\nsur les valeurs `[8, 4, 9]`, et `sum` calcule\nleur total : $8 + 4 + 9 = 21$.\n"
            },
            {
              "text": "$3$",
              "correct": false,
              "feedback": "Erreur : ce serait le nombre de **clés**, soit\nla longueur du dictionnaire (`len(stock)`).\nLa somme demandée porte sur les **valeurs**, pas\nsur le nombre d'entrées.\n"
            }
          ],
          "explanation": "Trois vues complémentaires sur un dictionnaire :\n`d.keys()` (les clés), `d.values()` (les valeurs)\net `d.items()` (les couples). Combinées avec les\nfonctions natives `sum`, `max`, `min` ou `len`,\nelles permettent d'écrire des traitements concis."
        },
        {
          "statement": "On dispose du dictionnaire suivant :\n```python\nstock = {\"stylo\": 2, \"cahier\": 15, \"gomme\": 6}\n```\nQue renvoie l'expression `sum(stock.values())` ?",
          "options": [
            {
              "text": "La somme des longueurs des clés (16)",
              "correct": false,
              "feedback": "Erreur : `values` agit sur les valeurs, pas sur\nles clés. La longueur des chaînes utilisées\ncomme clés n'intervient pas ici.\n"
            },
            {
              "text": "Une erreur de type",
              "correct": false,
              "feedback": "Erreur : la fonction `sum` accepte parfaitement\nun itérable de nombres. La méthode `values`\nrenvoie justement un itérable, donc\nl'opération est valide.\n"
            },
            {
              "text": "$23$",
              "correct": true,
              "feedback": "Bonne réponse : `stock.values()` renvoie une vue\nsur les valeurs `[2, 15, 6]`, et `sum` calcule\nleur total : $2 + 15 + 6 = 23$.\n"
            },
            {
              "text": "$3$",
              "correct": false,
              "feedback": "Erreur : ce serait le nombre de **clés**, soit\nla longueur du dictionnaire (`len(stock)`).\nLa somme demandée porte sur les **valeurs**, pas\nsur le nombre d'entrées.\n"
            }
          ],
          "explanation": "Trois vues complémentaires sur un dictionnaire :\n`d.keys()` (les clés), `d.values()` (les valeurs)\net `d.items()` (les couples). Combinées avec les\nfonctions natives `sum`, `max`, `min` ou `len`,\nelles permettent d'écrire des traitements concis."
        }
      ]
    },
    {
      "id": "q27",
      "difficulty": 3,
      "skills": [
        "inversion",
        "items"
      ],
      "title": "Inverser un dictionnaire",
      "partie": "parcours",
      "statement": "On souhaite, à partir d'un dictionnaire `d` aux\nvaleurs distinctes, construire un nouveau\ndictionnaire `inv` où les clés et les valeurs sont\néchangées. Quel code convient ?",
      "options": [
        {
          "text": "```python\ninv = {}\nfor k in d:\n    inv[v] = k\n```\n",
          "correct": false,
          "feedback": "Erreur : la boucle `for k in d` ne fournit que\nla clé `k` ; la variable `v` n'est définie nulle\npart et Python lève `NameError`. Il faut\nobtenir la valeur par `d[k]` ou par `items()`.\n"
        },
        {
          "text": "```python\ninv = {}\nfor v in d.values():\n    inv[v] = d[v]\n```\n",
          "correct": false,
          "feedback": "Erreur : la boucle parcourt les **valeurs** de\n`d`, et `d[v]` cherche une valeur parmi les\nclés, d'où une `KeyError` dès le premier tour.\nLes valeurs ne sont pas des clés de `d`.\n"
        },
        {
          "text": "```python\ninv = {}\nfor k, v in d.items():\n    inv[v] = k\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : `items()` fournit chaque couple\n`(clé, valeur)`, dépaqueté dans `k` et `v`.\nL'affectation `inv[v] = k` range le couple\ninversé dans le nouveau dictionnaire.\n"
        },
        {
          "text": "```python\ninv = {}\nfor k, v in d.items():\n    inv[k] = v\n```\n",
          "correct": false,
          "feedback": "Erreur : l'affectation `inv[k] = v` recopie\nchaque couple dans le même sens. On obtient une\ncopie de `d`, pas son inverse ; il faut\néchanger les rôles avec `inv[v] = k`.\n"
        }
      ],
      "explanation": "Cette technique ne fonctionne correctement que si\nles valeurs sont uniques. Si plusieurs clés\npartagent une même valeur, chaque affectation\n`inv[v] = k` écrase celle déjà rangée sous cette\nclé et une seule association survit. Pour un mappage\nmultivalué, il faudrait construire un dictionnaire\nde listes."
    },
    {
      "id": "q28",
      "difficulty": 2,
      "skills": [
        "construction",
        "p-uplets"
      ],
      "title": "Des couples retournés en dictionnaire",
      "partie": "bases",
      "statement": "On construit un dictionnaire à partir d'une liste de couples :\n\n```python\ncouples = [(\"rouge\", 1), (\"vert\", 2), (\"bleu\", 3)]\ncouleurs = {}\nfor c in couples:\n    couleurs[c[1]] = c[0]\nprint(couleurs[2])\n```\n\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`vert`",
          "correct": true,
          "feedback": "Bonne réponse : `c[1]` est le numéro et `c[0]` le nom,\ndonc la boucle construit\n`{1: 'rouge', 2: 'vert', 3: 'bleu'}`. La clé `2` est\nassociée à `\"vert\"`.\n"
        },
        {
          "text": "`bleu`",
          "correct": false,
          "feedback": "Erreur : `couleurs[2]` ne désigne pas la troisième\nentrée, comme dans une liste, mais la valeur associée\nà la clé `2`, soit `\"vert\"`.\n"
        },
        {
          "text": "`2`",
          "correct": false,
          "feedback": "Erreur : `2` est la clé cherchée, pas la valeur.\nL'affectation `couleurs[c[1]] = c[0]` associe à chaque\nnuméro le nom de la couleur.\n"
        },
        {
          "text": "Il lève une `KeyError`",
          "correct": false,
          "feedback": "Erreur : ce serait le cas si les noms servaient de\nclés. Ici, `couleurs[c[1]] = c[0]` prend les numéros\ncomme clés, et la clé `2` existe bien.\n"
        }
      ],
      "explanation": "Dans `couleurs[c[1]] = c[0]`, l'expression entre crochets est\nla clé : chaque couple (nom, numéro) donne une entrée qui\nassocie le numéro au nom. On lit ensuite une valeur par sa\nclé, jamais par sa position."
    },
    {
      "id": "q29",
      "difficulty": 3,
      "skills": [
        "synthese",
        "complexite",
        "recherche"
      ],
      "title": "Pourquoi un dictionnaire pour appartenir",
      "partie": "cles",
      "statement": "On dispose de dix mille valeurs distinctes et l'on doit,\npour chacune des nombreuses entrées suivantes lues au\nclavier, déterminer si la valeur saisie figure dans la\ncollection. En Python, il est nettement plus efficace\nde ranger ces dix mille valeurs comme clés d'un\n**dictionnaire** plutôt que dans une **liste**. Pourquoi ?",
      "options": [
        {
          "text": "Parce que le dictionnaire retrouve une clé directement, sans parcourir la collection",
          "correct": true,
          "feedback": "Bonne réponse : le test `saisie in d` retrouve la clé\nsans examiner les autres valeurs. Une liste, au\ncontraire, est parcourue séquentiellement : dans le\npire des cas, les dix mille valeurs sont examinées,\navec un coût linéaire.\n"
        },
        {
          "text": "Parce qu'un dictionnaire occupe systématiquement moins de mémoire qu'une liste de même taille",
          "correct": false,
          "feedback": "Erreur : c'est plutôt l'inverse. Un dictionnaire est\nplus coûteux en mémoire qu'une liste de même taille.\nLe gain se situe sur la vitesse de recherche, pas sur\nla place occupée.\n"
        },
        {
          "text": "Parce qu'un dictionnaire est automatiquement trié, ce qui rend la recherche dichotomique",
          "correct": false,
          "feedback": "Erreur : un dictionnaire n'est pas trié sur ses clés,\net aucune recherche dichotomique n'y est effectuée.\nL'accès rapide vient de ce que la clé est retrouvée\ndirectement.\n"
        },
        {
          "text": "Parce qu'une liste interdit les valeurs en double et que ce contrôle ralentit chaque recherche",
          "correct": false,
          "feedback": "Erreur : une liste accepte parfaitement les\ndoublons ; ce sont les clés d'un dictionnaire qui\nsont uniques. Et aucun contrôle d'unicité\nn'intervient dans une recherche.\n"
        }
      ],
      "explanation": "En pratique, on range les dix mille valeurs comme clés\nd'un dictionnaire (par exemple `d[valeur] = True` dans\nune boucle), puis chaque test s'écrit `saisie in d`,\nsans parcourir la collection."
    },
    {
      "id": "q30",
      "difficulty": 3,
      "skills": [
        "synthese",
        "cles-immuables"
      ],
      "title": "Liste impossible comme clé",
      "partie": "cles",
      "statement": "L'instruction `d = {[1, 2]: \"premier\"}` provoque une\nerreur `TypeError`. En revanche,\n`d = {(1, 2): \"premier\"}` est acceptée. Pourquoi cette\ndifférence ?",
      "options": [
        {
          "text": "Parce qu'une liste est modifiable, alors qu'une clé doit rester fixe, comme un tuple",
          "correct": true,
          "feedback": "Bonne réponse : si une clé pouvait être modifiée après\nson insertion, l'entrée correspondante ne serait plus\nretrouvée. C'est pourquoi Python n'autorise que des\nobjets **immuables** comme clés, comme les tuples.\n"
        },
        {
          "text": "Parce qu'une liste occupe trop de mémoire alors qu'un tuple n'en utilise pas",
          "correct": false,
          "feedback": "Erreur : le tuple consomme également de la mémoire,\nsimplement un peu moins qu'une liste. La raison du\nrefus n'est pas la taille mais la mutabilité.\n"
        },
        {
          "text": "Parce qu'une liste ne peut contenir que des entiers, alors qu'un tuple peut contenir tout type de valeur",
          "correct": false,
          "feedback": "Erreur : une liste peut contenir n'importe quel\ntype de valeur en Python (entiers, chaînes,\nobjets, autres listes, etc.).\n"
        },
        {
          "text": "Parce que les tuples sont, en Python, les seuls objets autorisés comme clés",
          "correct": false,
          "feedback": "Erreur : beaucoup d'autres objets servent de clés en\nPython (entiers, flottants, chaînes, booléens). Le\ncritère est l'immuabilité, pas l'appartenance à la\nfamille des tuples.\n"
        }
      ],
      "explanation": "Règle générale en Python : seuls les objets immuables\npeuvent servir de clés de dictionnaire. Les types\nusuels acceptés sont les entiers, les flottants, les\nchaînes, les booléens et les tuples dont les éléments\nsont eux-mêmes immuables. Les listes et les\ndictionnaires, modifiables, ne peuvent pas être des\nclés."
    },
    {
      "id": "q31",
      "difficulty": 1,
      "skills": [
        "p-uplet-nomme",
        "enregistrement",
        "definition"
      ],
      "title": "Représenter un p-uplet nommé",
      "partie": "bases",
      "statement": "Un **p-uplet nommé** (ou enregistrement) regroupe\ndes valeurs de types différents, chacune désignée\npar un nom de champ, par exemple le nom et l'âge\nd'une personne. Comment le représente-t-on en\nPython ?",
      "options": [
        {
          "text": "Par un dictionnaire dont les clés sont les noms des champs : `{\"nom\": \"Dupont\", \"age\": 17}`",
          "correct": true,
          "feedback": "Chaque champ est une clé, chaque valeur est\naccessible par ce nom.\n"
        },
        {
          "text": "Par un tuple qui alterne les noms et les valeurs des champs : `(\"nom\", \"Dupont\", \"age\", 17)`",
          "correct": false,
          "feedback": "Un tuple n'associe pas un nom à chaque valeur ;\nil faudrait retenir des positions.\n"
        },
        {
          "text": "Par une liste qui ne contient que les noms des champs : `[\"nom\", \"age\"]`",
          "correct": false,
          "feedback": "Les valeurs manquent ; une liste de noms ne\ndécrit pas une personne.\n"
        },
        {
          "text": "Par une chaîne de caractères qui concatène noms et valeurs : `\"nom=Dupont;age=17\"`",
          "correct": false,
          "feedback": "On ne pourrait accéder à un champ qu'en découpant\nla chaîne ; le dictionnaire le fait directement.\n"
        }
      ],
      "explanation": "Le programme de première présente les p-uplets\nnommés comme des dictionnaires : les clés jouent le\nrôle des noms de champs. Une table de données est\nalors une liste de tels dictionnaires, tous avec les\nmêmes clés."
    },
    {
      "id": "q32",
      "difficulty": 2,
      "skills": [
        "p-uplet-nomme",
        "acces",
        "exif"
      ],
      "title": "Lire un champ des données d'une photo",
      "partie": "bases",
      "statement": "Les données d'une photo sont rangées dans un\nenregistrement :\n\n```python\nphoto = {\"largeur\": 4000, \"hauteur\": 3000, \"iso\": 200, \"date\": \"2026-07-14\"}\n```\n\nQuelle expression donne la hauteur de la photo ?",
      "options": [
        {
          "text": "`photo[\"hauteur\"]`",
          "correct": true,
          "feedback": "L'accès se fait par la clé, le nom du champ.\n"
        },
        {
          "text": "`photo[1]`",
          "correct": false,
          "feedback": "Il n'y a pas de clé $1$ : l'expression lève une\n`KeyError`. Un dictionnaire ne se lit pas par\nposition.\n"
        },
        {
          "text": "`photo.hauteur`",
          "correct": false,
          "feedback": "Cette écriture lève une `AttributeError` : un\ndictionnaire n'a pas d'attribut nommé comme ses\nclés.\n"
        },
        {
          "text": "`photo(\"hauteur\")`",
          "correct": false,
          "feedback": "Les parenthèses tentent d'appeler le dictionnaire\ncomme une fonction : `TypeError`.\n"
        }
      ],
      "explanation": "Un enregistrement se lit par le nom de ses champs :\n`photo[\"hauteur\"]` vaut $3000$. Les données EXIF\nd'une image (dimensions, sensibilité, date de prise\nde vue) se prêtent bien à cette représentation."
    },
    {
      "id": "q33",
      "difficulty": 3,
      "skills": [
        "p-uplet-nomme",
        "liste-de-dictionnaires",
        "parcours",
        "maximum"
      ],
      "title": "Le meilleur élève d'une liste d'enregistrements",
      "partie": "bases",
      "statement": "```python\neleves = [{\"nom\": \"Lina\", \"note\": 14},\n          {\"nom\": \"Noah\", \"note\": 9},\n          {\"nom\": \"Ava\", \"note\": 17}]\nmeilleur = eleves[0]\nfor e in eleves:\n    if e[\"note\"] > meilleur[\"note\"]:\n        meilleur = e\nprint(meilleur[\"nom\"])\n```\n\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`Ava`",
          "correct": true,
          "feedback": "`meilleur` prend l'enregistrement de plus haute\nnote ; `Ava` a $17$.\n"
        },
        {
          "text": "`Lina`",
          "correct": false,
          "feedback": "`Lina` est seulement le point de départ ; elle\nest dépassée par `Ava` au troisième tour.\n"
        },
        {
          "text": "`17`",
          "correct": false,
          "feedback": "Le programme affiche le champ `nom` de\nl'enregistrement retenu, pas sa note.\n"
        },
        {
          "text": "`{\"nom\": \"Ava\", \"note\": 17}`",
          "correct": false,
          "feedback": "`print` reçoit `meilleur[\"nom\"]`, une chaîne, pas\nl'enregistrement entier.\n"
        }
      ],
      "explanation": "Chaque élément de la liste est un enregistrement ;\nla recherche du maximum compare un champ (`note`)\net conserve l'enregistrement entier, d'où l'accès\nfinal au champ `nom`."
    },
    {
      "id": "q34",
      "difficulty": 2,
      "skills": [
        "cles",
        "type",
        "entier-chaine"
      ],
      "title": "Une clé entière et une clé chaîne",
      "partie": "cles",
      "statement": "Qu'affiche ce programme ?\n```python\nd = {1: \"un\", \"1\": \"one\"}\nprint(len(d), d[1])\n```",
      "options": [
        {
          "text": "`2 un`",
          "correct": true,
          "feedback": "Bonne réponse : l'entier `1` et la chaîne `\"1\"` sont deux\nclés différentes. Le dictionnaire a deux entrées, et `d[1]`\nlit celle de la clé entière.\n"
        },
        {
          "text": "`1 one`",
          "correct": false,
          "feedback": "Erreur : les deux clés ne se confondent pas, aucune valeur\nn'écrase l'autre.\n"
        },
        {
          "text": "`2 one`",
          "correct": false,
          "feedback": "Erreur : `d[1]` sans guillemets vise la clé entière, associée\nà `\"un\"`.\n"
        },
        {
          "text": "Une erreur, deux clés ne pouvant avoir la même écriture",
          "correct": false,
          "feedback": "Erreur : `1` et `\"1\"` n'ont pas la même écriture ni le même\ntype.\n"
        }
      ],
      "explanation": "Le type d'une clé compte : une saisie `input`, toujours une chaîne,\nne retrouve pas une entrée à clé entière sans conversion.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\nd = {1: \"un\", \"1\": \"one\"}\nprint(len(d), d[1])\n```",
          "options": [
            {
              "text": "`2 un`",
              "correct": true,
              "feedback": "Bonne réponse : l'entier `1` et la chaîne `\"1\"` sont deux\nclés différentes. Le dictionnaire a deux entrées, et `d[1]`\nlit celle de la clé entière.\n"
            },
            {
              "text": "`1 one`",
              "correct": false,
              "feedback": "Erreur : les deux clés ne se confondent pas, aucune valeur\nn'écrase l'autre.\n"
            },
            {
              "text": "`2 one`",
              "correct": false,
              "feedback": "Erreur : `d[1]` sans guillemets vise la clé entière, associée\nà `\"un\"`.\n"
            },
            {
              "text": "Une erreur, deux clés ne pouvant avoir la même écriture",
              "correct": false,
              "feedback": "Erreur : `1` et `\"1\"` n'ont pas la même écriture ni le même\ntype.\n"
            }
          ],
          "explanation": "Le type d'une clé compte : une saisie `input`, toujours une chaîne,\nne retrouve pas une entrée à clé entière sans conversion."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nd = {7: \"sept\", \"7\": \"seven\"}\nprint(len(d), d[7])\n```",
          "options": [
            {
              "text": "`2 sept`",
              "correct": true,
              "feedback": "Bonne réponse : l'entier `7` et la chaîne `\"7\"` sont deux\nclés différentes. Le dictionnaire a deux entrées, et `d[7]`\nlit celle de la clé entière.\n"
            },
            {
              "text": "`1 seven`",
              "correct": false,
              "feedback": "Erreur : les deux clés ne se confondent pas, aucune valeur\nn'écrase l'autre.\n"
            },
            {
              "text": "`2 seven`",
              "correct": false,
              "feedback": "Erreur : `d[7]` sans guillemets vise la clé entière, associée\nà `\"sept\"`.\n"
            },
            {
              "text": "Une erreur, deux clés ne pouvant avoir la même écriture",
              "correct": false,
              "feedback": "Erreur : `7` et `\"7\"` n'ont pas la même écriture ni le même\ntype.\n"
            }
          ],
          "explanation": "Le type d'une clé compte : une saisie `input`, toujours une chaîne,\nne retrouve pas une entrée à clé entière sans conversion."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nd = {3: \"trois\", \"3\": \"three\"}\nprint(len(d), d[3])\n```",
          "options": [
            {
              "text": "`2 trois`",
              "correct": true,
              "feedback": "Bonne réponse : l'entier `3` et la chaîne `\"3\"` sont deux\nclés différentes. Le dictionnaire a deux entrées, et `d[3]`\nlit celle de la clé entière.\n"
            },
            {
              "text": "`1 three`",
              "correct": false,
              "feedback": "Erreur : les deux clés ne se confondent pas, aucune valeur\nn'écrase l'autre.\n"
            },
            {
              "text": "`2 three`",
              "correct": false,
              "feedback": "Erreur : `d[3]` sans guillemets vise la clé entière, associée\nà `\"trois\"`.\n"
            },
            {
              "text": "Une erreur, deux clés ne pouvant avoir la même écriture",
              "correct": false,
              "feedback": "Erreur : `3` et `\"3\"` n'ont pas la même écriture ni le même\ntype.\n"
            }
          ],
          "explanation": "Le type d'une clé compte : une saisie `input`, toujours une chaîne,\nne retrouve pas une entrée à clé entière sans conversion."
        }
      ]
    },
    {
      "id": "q35",
      "difficulty": 2,
      "skills": [
        "cles",
        "tuple",
        "coordonnees"
      ],
      "title": "Des coordonnées comme clé",
      "partie": "cles",
      "statement": "Pour associer une couleur à chaque case d'une grille repérée par\nses coordonnées, Lina écrit `grille[(2, 3)] = \"rouge\"`. Pourquoi ce\nchoix fonctionne-t-il ?",
      "options": [
        {
          "text": "Parce qu'un tuple est immuable, donc accepté comme clé",
          "correct": true,
          "feedback": "Bonne réponse : les clés doivent être immuables. Un tuple de\ndeux entiers désigne une case de façon unique et ne peut pas\nchanger.\n"
        },
        {
          "text": "Parce que Python convertit le tuple en chaîne `\"2, 3\"`",
          "correct": false,
          "feedback": "Erreur : aucune conversion n'a lieu, la clé reste le tuple.\n"
        },
        {
          "text": "Parce que les dictionnaires n'acceptent que des tuples",
          "correct": false,
          "feedback": "Erreur : entiers, chaînes et tuples sont tous acceptés.\n"
        },
        {
          "text": "Cela ne fonctionne pas, une clé devant être un entier ou une chaîne",
          "correct": false,
          "feedback": "Erreur : un tuple est une clé valide, contrairement à une liste.\n"
        }
      ],
      "explanation": "Un tuple immuable fait une clé composée commode : coordonnées,\ncouples nom-prénom, dates."
    },
    {
      "id": "q36",
      "difficulty": 3,
      "skills": [
        "cles",
        "flottant",
        "egalite"
      ],
      "title": "Une clé flottante introuvable",
      "partie": "cles",
      "statement": "Qu'affiche ce programme ?\n```python\nd = {0.3: \"trois dixièmes\"}\nprint(0.1 + 0.2 in d)\n```",
      "options": [
        {
          "text": "`False`, `0.1 + 0.2` n'étant pas exactement `0.3`",
          "correct": true,
          "feedback": "Bonne réponse : la recherche d'une clé repose sur l'égalité\nexacte. Deux flottants presque égaux sont deux clés différentes.\n"
        },
        {
          "text": "`True`, les deux valeurs étant égales à `0.3`",
          "correct": false,
          "feedback": "Erreur : en binaire, `0.1 + 0.2` n'est pas exactement `0.3`.\n"
        },
        {
          "text": "Une erreur, un flottant ne pouvant être une clé",
          "correct": false,
          "feedback": "Erreur : un flottant est immuable, donc accepté comme clé ; il\nest simplement peu fiable.\n"
        },
        {
          "text": "`trois dixièmes`, la valeur associée",
          "correct": false,
          "feedback": "Erreur : `in` renvoie un booléen, pas une valeur.\n"
        }
      ],
      "explanation": "Les flottants font de mauvaises clés : leurs approximations rendent\nles recherches imprévisibles. On préfère des entiers ou des\nchaînes."
    },
    {
      "id": "q37",
      "difficulty": 2,
      "skills": [
        "cles",
        "doublon",
        "creation"
      ],
      "title": "Une clé écrite deux fois à la création",
      "partie": "cles",
      "statement": "Qu'affiche ce programme ?\n```python\nstock = {\"pommes\": 10, \"poires\": 4, \"pommes\": 25}\nprint(len(stock), stock[\"pommes\"])\n```",
      "options": [
        {
          "text": "`2 25`",
          "correct": true,
          "feedback": "Bonne réponse : une clé est unique ; la seconde valeur\nassociée à `\"pommes\"` remplace la première. Il reste deux clés.\n"
        },
        {
          "text": "`3 10`",
          "correct": false,
          "feedback": "Erreur : un dictionnaire ne peut pas contenir deux fois la même\nclé.\n"
        },
        {
          "text": "`2 10`",
          "correct": false,
          "feedback": "Erreur : c'est la dernière valeur écrite qui l'emporte.\n"
        },
        {
          "text": "Une erreur, une clé répétée étant interdite",
          "correct": false,
          "feedback": "Erreur : Python accepte l'écriture et garde la dernière valeur,\nsans avertissement.\n"
        }
      ],
      "explanation": "Écrire deux fois une clé est presque toujours une faute de frappe ;\nPython ne la signale pas, d'où l'intérêt de relire.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\nstock = {\"pommes\": 10, \"poires\": 4, \"pommes\": 25}\nprint(len(stock), stock[\"pommes\"])\n```",
          "options": [
            {
              "text": "`2 25`",
              "correct": true,
              "feedback": "Bonne réponse : une clé est unique ; la seconde valeur\nassociée à `\"pommes\"` remplace la première. Il reste deux clés.\n"
            },
            {
              "text": "`3 10`",
              "correct": false,
              "feedback": "Erreur : un dictionnaire ne peut pas contenir deux fois la même\nclé.\n"
            },
            {
              "text": "`2 10`",
              "correct": false,
              "feedback": "Erreur : c'est la dernière valeur écrite qui l'emporte.\n"
            },
            {
              "text": "Une erreur, une clé répétée étant interdite",
              "correct": false,
              "feedback": "Erreur : Python accepte l'écriture et garde la dernière valeur,\nsans avertissement.\n"
            }
          ],
          "explanation": "Écrire deux fois une clé est presque toujours une faute de frappe ;\nPython ne la signale pas, d'où l'intérêt de relire."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nstock = {\"pommes\": 3, \"poires\": 4, \"pommes\": 8}\nprint(len(stock), stock[\"pommes\"])\n```",
          "options": [
            {
              "text": "`2 8`",
              "correct": true,
              "feedback": "Bonne réponse : une clé est unique ; la seconde valeur\nassociée à `\"pommes\"` remplace la première. Il reste deux clés.\n"
            },
            {
              "text": "`3 3`",
              "correct": false,
              "feedback": "Erreur : un dictionnaire ne peut pas contenir deux fois la même\nclé.\n"
            },
            {
              "text": "`2 3`",
              "correct": false,
              "feedback": "Erreur : c'est la dernière valeur écrite qui l'emporte.\n"
            },
            {
              "text": "Une erreur, une clé répétée étant interdite",
              "correct": false,
              "feedback": "Erreur : Python accepte l'écriture et garde la dernière valeur,\nsans avertissement.\n"
            }
          ],
          "explanation": "Écrire deux fois une clé est presque toujours une faute de frappe ;\nPython ne la signale pas, d'où l'intérêt de relire."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nstock = {\"pommes\": 50, \"poires\": 4, \"pommes\": 7}\nprint(len(stock), stock[\"pommes\"])\n```",
          "options": [
            {
              "text": "`2 7`",
              "correct": true,
              "feedback": "Bonne réponse : une clé est unique ; la seconde valeur\nassociée à `\"pommes\"` remplace la première. Il reste deux clés.\n"
            },
            {
              "text": "`3 50`",
              "correct": false,
              "feedback": "Erreur : un dictionnaire ne peut pas contenir deux fois la même\nclé.\n"
            },
            {
              "text": "`2 50`",
              "correct": false,
              "feedback": "Erreur : c'est la dernière valeur écrite qui l'emporte.\n"
            },
            {
              "text": "Une erreur, une clé répétée étant interdite",
              "correct": false,
              "feedback": "Erreur : Python accepte l'écriture et garde la dernière valeur,\nsans avertissement.\n"
            }
          ],
          "explanation": "Écrire deux fois une clé est presque toujours une faute de frappe ;\nPython ne la signale pas, d'où l'intérêt de relire."
        }
      ]
    },
    {
      "id": "q38",
      "difficulty": 1,
      "skills": [
        "parcours",
        "ordre",
        "insertion"
      ],
      "title": "L'ordre de parcours d'un dictionnaire",
      "partie": "parcours",
      "statement": "Dans quel ordre `for cle in d:` parcourt-il les clés d'un\ndictionnaire Python ?",
      "options": [
        {
          "text": "Dans l'ordre où les clés ont été ajoutées",
          "correct": true,
          "feedback": "Bonne réponse : depuis Python 3.7, un dictionnaire conserve\nl'ordre d'insertion de ses clés.\n"
        },
        {
          "text": "Dans l'ordre alphabétique des clés",
          "correct": false,
          "feedback": "Erreur : aucun tri n'est fait ; pour un ordre alphabétique, on\nutilise `sorted(d)`.\n"
        },
        {
          "text": "Dans un ordre aléatoire, différent à chaque exécution",
          "correct": false,
          "feedback": "Erreur : l'ordre est stable et prévisible.\n"
        },
        {
          "text": "Par valeurs croissantes",
          "correct": false,
          "feedback": "Erreur : le parcours porte sur les clés, dans leur ordre\nd'ajout.\n"
        }
      ],
      "explanation": "Un dictionnaire se parcourt dans l'ordre d'insertion ; pour un\nautre ordre, on trie explicitement les clés."
    },
    {
      "id": "q39",
      "difficulty": 2,
      "skills": [
        "parcours",
        "valeurs",
        "comptage"
      ],
      "title": "Compter les valeurs au-dessus d'un seuil",
      "partie": "parcours",
      "statement": "Qu'affiche ce programme ?\n```python\nnotes = {\"Lou\": 12, \"Sami\": 9, \"Inès\": 15}\nn = 0\nfor prenom in notes:\n    if notes[prenom] > 10:\n        n = n + 1\nprint(n)\n```",
      "options": [
        {
          "text": "`2`",
          "correct": true,
          "feedback": "Bonne réponse : la boucle parcourt les clés, lit chaque valeur\navec `notes[prenom]` et compte celles qui dépassent 10.\n"
        },
        {
          "text": "`3`",
          "correct": false,
          "feedback": "Erreur : recompte les notes strictement supérieures à 10, il y\nen a 2.\n"
        },
        {
          "text": "`0`",
          "correct": false,
          "feedback": "Erreur : au moins une note dépasse 10.\n"
        },
        {
          "text": "Une erreur, `prenom` étant une clé et non une note",
          "correct": false,
          "feedback": "Erreur : `notes[prenom]` donne bien la note associée à la clé.\n"
        }
      ],
      "explanation": "Parcourir les clés et accéder aux valeurs par `d[cle]` est le\nschéma de base pour compter ou cumuler dans un dictionnaire.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\nnotes = {\"Lou\": 12, \"Sami\": 9, \"Inès\": 15}\nn = 0\nfor prenom in notes:\n    if notes[prenom] > 10:\n        n = n + 1\nprint(n)\n```",
          "options": [
            {
              "text": "`2`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle parcourt les clés, lit chaque valeur\navec `notes[prenom]` et compte celles qui dépassent 10.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : recompte les notes strictement supérieures à 10, il y\nen a 2.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : au moins une note dépasse 10.\n"
            },
            {
              "text": "Une erreur, `prenom` étant une clé et non une note",
              "correct": false,
              "feedback": "Erreur : `notes[prenom]` donne bien la note associée à la clé.\n"
            }
          ],
          "explanation": "Parcourir les clés et accéder aux valeurs par `d[cle]` est le\nschéma de base pour compter ou cumuler dans un dictionnaire."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nnotes = {\"Lou\": 12, \"Sami\": 9, \"Inès\": 15}\nn = 0\nfor prenom in notes:\n    if notes[prenom] > 14:\n        n = n + 1\nprint(n)\n```",
          "options": [
            {
              "text": "`1`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle parcourt les clés, lit chaque valeur\navec `notes[prenom]` et compte celles qui dépassent 14.\n"
            },
            {
              "text": "`2`",
              "correct": false,
              "feedback": "Erreur : recompte les notes strictement supérieures à 14, il y\nen a 1.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : au moins une note dépasse 14.\n"
            },
            {
              "text": "Une erreur, `prenom` étant une clé et non une note",
              "correct": false,
              "feedback": "Erreur : `notes[prenom]` donne bien la note associée à la clé.\n"
            }
          ],
          "explanation": "Parcourir les clés et accéder aux valeurs par `d[cle]` est le\nschéma de base pour compter ou cumuler dans un dictionnaire."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nnotes = {\"Lou\": 12, \"Sami\": 9, \"Inès\": 15}\nn = 0\nfor prenom in notes:\n    if notes[prenom] > 8:\n        n = n + 1\nprint(n)\n```",
          "options": [
            {
              "text": "`3`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle parcourt les clés, lit chaque valeur\navec `notes[prenom]` et compte celles qui dépassent 8.\n"
            },
            {
              "text": "`4`",
              "correct": false,
              "feedback": "Erreur : recompte les notes strictement supérieures à 8, il y\nen a 3.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : au moins une note dépasse 8.\n"
            },
            {
              "text": "Une erreur, `prenom` étant une clé et non une note",
              "correct": false,
              "feedback": "Erreur : `notes[prenom]` donne bien la note associée à la clé.\n"
            }
          ],
          "explanation": "Parcourir les clés et accéder aux valeurs par `d[cle]` est le\nschéma de base pour compter ou cumuler dans un dictionnaire."
        }
      ]
    },
    {
      "id": "q40",
      "difficulty": 3,
      "skills": [
        "parcours",
        "items",
        "filtre",
        "construction"
      ],
      "title": "Construire un dictionnaire filtré",
      "partie": "parcours",
      "statement": "Qu'affiche ce programme ?\n```python\nprix = {\"stylo\": 2, \"cahier\": 5, \"gomme\": 1}\npas_cher = {}\nfor article, p in prix.items():\n    if p < 3:\n        pas_cher[article] = p\nprint(pas_cher)\n```",
      "options": [
        {
          "text": "`{'stylo': 2, 'gomme': 1}`",
          "correct": true,
          "feedback": "Bonne réponse : `items` fournit chaque couple clé-valeur ; les\ncouples dont le prix est inférieur à 3 sont recopiés dans le\nnouveau dictionnaire.\n"
        },
        {
          "text": "`{'cahier': 5}`",
          "correct": false,
          "feedback": "Erreur : la condition garde les prix inférieurs à 3, pas\nsupérieurs.\n"
        },
        {
          "text": "`['stylo', 'gomme']`",
          "correct": false,
          "feedback": "Erreur : `pas_cher` est un dictionnaire, les valeurs sont\nconservées avec les clés.\n"
        },
        {
          "text": "`{'stylo': 2, 'cahier': 5, 'gomme': 1}`",
          "correct": false,
          "feedback": "Erreur : le `if` écarte le cahier.\n"
        }
      ],
      "explanation": "Filtrer un dictionnaire, c'est en construire un nouveau en ne\nrecopiant que les couples qui vérifient une condition."
    }
  ]
}