{
  "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.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Notion de dictionnaire",
      "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": "L'implémentation interne d'un\ndictionnaire repose sur une table\nde hachage. L'accès à une valeur\npar sa clé est en moyenne en\n$O(1)$. Depuis Python 3.7,\nl'ordre d'insertion des paires\nest conservé."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "creation"
      ],
      "title": "Création",
      "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é",
      "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",
      "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",
      "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",
      "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é",
      "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",
      "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",
      "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),\nmémoïsation (entrée vers\nrésultat), configuration\n(option vers valeur)."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "acces",
        "cle-variable"
      ],
      "title": "Clé fournie par une variable",
      "statement": "Que produit ce code ?\n\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`."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "comptage"
      ],
      "title": "Compter les occurrences",
      "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",
      "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",
      "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",
      "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",
      "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 : l'accès par clé est en\n$O(1)$ en moyenne, contre $O(n)$ pour\nparcourir une liste. Sur de gros volumes de\ndonnées, la différence est\nconsidérable. Pour un\nmillion d'éléments, on\nparle d'environ $1$ µs\navec un dictionnaire,\ncontre plusieurs\nmillisecondes avec une\nliste.\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\n(table de hachage interne),\nmais offre une recherche\ntrès rapide. La liste, plus\ncompacte, est lente pour la\nrecherche par clé."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "exemple-traduction"
      ],
      "title": "Représenter une table de traduction",
      "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",
      "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."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "ordre-dict"
      ],
      "title": "Ordre des entrées",
      "statement": "Depuis Python 3.7, dans quel\nordre les entrées d'un\ndictionnaire sont-elles\nparcourues ?",
      "options": [
        {
          "text": "Dans un ordre aléatoire à chaque exécution",
          "correct": false,
          "feedback": "L'ordre est en réalité\ndéterministe depuis\nPython 3.7 : les\nentrées sont parcourues\ndans l'ordre où elles\nont été insérées.\n"
        },
        {
          "text": "Dans l'ordre d'insertion des paires",
          "correct": true,
          "feedback": "C'est un changement\nsubtil mais important.\nAvant Python 3.7,\nl'ordre n'était pas\ngaranti, ce qui pouvait\nsurprendre.\n"
        },
        {
          "text": "Dans l'ordre alphabétique des clés",
          "correct": false,
          "feedback": "Aucun tri automatique sur\nles clés n'est effectué.\n"
        },
        {
          "text": "Par taille croissante des valeurs",
          "correct": false,
          "feedback": "Aucun tri automatique\nn'est effectué sur les\nvaleurs.\n"
        }
      ],
      "explanation": "Pour un parcours **trié par clé**, on écrit\n`for k in sorted(d):`. Pour\nun parcours **trié par valeur**, on écrit\n`for k in sorted(d, key=d.get):`."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "vide"
      ],
      "title": "Cas du dictionnaire vide",
      "statement": "Que vaut `bool({})` ?",
      "options": [
        {
          "text": "Une exception",
          "correct": false,
          "feedback": "Le code est parfaitement\nvalide.\n"
        },
        {
          "text": "`False`",
          "correct": true,
          "feedback": "C'est la convention\nPython : les structures\nvides sont fausses.\nC'est utile pour\ntester rapidement avec\n`if d:`, qui se lit\n« si le dictionnaire\nn'est pas vide ».\n"
        },
        {
          "text": "La valeur $0$",
          "correct": false,
          "feedback": "La fonction `bool`\nrenvoie un booléen, pas\nun entier.\n"
        },
        {
          "text": "`True`",
          "correct": false,
          "feedback": "Un dictionnaire vide est\nconsidéré comme faux en\nPython, au même titre que\n`[]`, `\"\"`, $0$ et\n`None`.\n"
        }
      ],
      "explanation": "Convention dite « truthy /\nfalsy » : sont **faux**\n`False`, $0$, $0.0$, `\"\"`,\n`[]`, `{}`, `()`, et\n`None`. Tous les autres\nobjets sont considérés\ncomme **vrais** dans un\ncontexte booléen."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "piege-modification"
      ],
      "title": "Modifier un dictionnaire pendant son parcours",
      "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",
      "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": "L'accès par clé est en $O(1)$ en moyenne",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte. C'est la\nconséquence directe de\nl'implémentation par\ntable de hachage.\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 mutables,\nelles ne peuvent pas\nservir de clés. Seuls\nles objets immuables\n(entier, chaîne,\nflottant, tuple,\nensemble figé) peuvent\nêtre utilisés comme\nclés.\n"
        },
        {
          "text": "Depuis Python 3.7, l'ordre d'insertion des paires est conservé lors du parcours",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte. C'est une\ngarantie introduite à\npartir de Python 3.7.\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",
      "statement": "On dispose du dictionnaire suivant :\n\n```python\nstock = {\"stylo\": 5, \"cahier\": 12, \"gomme\": 3}\n```\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."
    },
    {
      "id": "q27",
      "difficulty": 3,
      "skills": [
        "inversion",
        "items"
      ],
      "title": "Inverser un dictionnaire",
      "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": [
        "dict",
        "conversion"
      ],
      "title": "Construction depuis une liste de couples",
      "statement": "Que renvoie l'expression\n`dict([(\"rouge\", 1), (\"vert\", 2), (\"bleu\", 3)])` ?",
      "options": [
        {
          "text": "`{'rouge': 1, 'vert': 2, 'bleu': 3}`",
          "correct": true,
          "feedback": "Bonne réponse : la fonction `dict` accepte une\nliste de couples `(clé, valeur)` et construit\nle dictionnaire correspondant. C'est une façon\ncourante de bâtir un dictionnaire à partir de\ndonnées structurées (par exemple des couples\nlus ligne par ligne dans un fichier).\n"
        },
        {
          "text": "`{'rouge', 'vert', 'bleu'}`",
          "correct": false,
          "feedback": "Erreur : des accolades sans deux-points\nformeraient un **ensemble**, un autre type de\ncollection qui n'est pas au programme. `dict`\nproduit un dictionnaire avec des clés **et**\ndes valeurs ; il ne se limite pas aux clés.\n"
        },
        {
          "text": "Une erreur de syntaxe",
          "correct": false,
          "feedback": "Erreur : la fonction `dict` est native en Python\net accepte plusieurs formes d'appel. La\nconstruction depuis une liste de couples est\nparfaitement valide.\n"
        },
        {
          "text": "`[('rouge', 1), ('vert', 2), ('bleu', 3)]`",
          "correct": false,
          "feedback": "Erreur : ce serait la liste d'origine inchangée.\nLa fonction native `dict` transforme cette liste\nen un véritable dictionnaire.\n"
        }
      ],
      "explanation": "Deux façons usuelles de créer un dictionnaire :\nle littéral `{\"a\": 1, \"b\": 2}` et la fonction\n`dict` appliquée à une liste de couples. Pour\nbâtir un dictionnaire à partir de deux listes\nparallèles `cles` et `valeurs`, on part d'un\ndictionnaire vide `d = {}` et l'on écrit\n`for i in range(len(cles)): d[cles[i]] = valeurs[i]`.\nLa forme « liste de couples » est très utile lors\nde la lecture de fichiers structurés."
    },
    {
      "id": "q29",
      "difficulty": 3,
      "skills": [
        "synthese",
        "complexite",
        "recherche"
      ],
      "title": "Pourquoi un dictionnaire pour appartenir",
      "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 placer ces dix mille valeurs dans un **dictionnaire**\n(ou un ensemble) plutôt que dans une **liste**. Pourquoi ?",
      "options": [
        {
          "text": "Parce que la recherche d'une clé est en moyenne à temps constant (table de hachage), alors qu'une liste parcourt tous ses éléments",
          "correct": true,
          "feedback": "Bonne réponse : un dictionnaire (ou un ensemble)\nrepose sur une **table de hachage**. Tester\nl'appartenance d'une valeur coûte en moyenne un\ntemps constant, indépendamment de la taille de la\ncollection. Une liste, au contraire, est parcourue\nséquentiellement, ce qui donne un coût proportionnel\nau nombre d'éléments dans le pire des cas.\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 (table interne avec des\ncases vides). Le gain se situe sur la vitesse de\nrecherche, pas sur l'empreinte mémoire.\n"
        },
        {
          "text": "Parce qu'un dictionnaire est automatiquement trié, ce qui rend la recherche dichotomique",
          "correct": false,
          "feedback": "Erreur : un dictionnaire Python n'est pas trié sur\nses clés (il préserve l'ordre d'insertion depuis\nPython $3.7$, ce qui est différent). L'accès rapide\nne vient pas du tri mais du hachage.\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 ensembles et les clés d'un\ndictionnaire qui sont uniques. Et le contrôle\nd'unicité n'est pas la raison de la rapidité.\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`.\nL'ensemble évoqué dans l'énoncé est un autre type\nPython, hors programme de première, qui ne stocke que\nles clés sans valeur associée ; il repose sur la même\nstructure interne, une table de hachage qui transforme\nchaque clé en un indice de tableau, d'où le temps moyen\nconstant."
    },
    {
      "id": "q30",
      "difficulty": 3,
      "skills": [
        "synthese",
        "cles-immuables",
        "hachage"
      ],
      "title": "Liste impossible comme clé",
      "statement": "L'instruction `d = {[1, 2]: \"premier\"}` provoque une\nerreur `TypeError: unhashable type: 'list'`. En revanche,\n`d = {(1, 2): \"premier\"}` est acceptée. Pourquoi cette\ndifférence ?",
      "options": [
        {
          "text": "Parce qu'une liste est modifiable : son hachage changerait et l'entrée deviendrait introuvable ; un tuple immuable a un hachage stable",
          "correct": true,
          "feedback": "Bonne réponse : un dictionnaire est implémenté par\nune table de hachage. Chaque clé est stockée à\nl'emplacement déterminé par sa **valeur de hachage**.\nSi une clé pouvait changer après insertion, l'entrée\nne serait plus retrouvée, et la table deviendrait\nincohérente. C'est pourquoi Python n'autorise que\ndes objets **immuables** comme clés.\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 ordonnés et hachables par construction",
          "correct": false,
          "feedback": "Erreur : beaucoup d'autres objets sont hachables\nen Python (entiers, flottants, chaînes, booléens).\nLe critère est l'immuabilité, pas l'appartenance\nà la famille 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 hachables. Les listes et les\ndictionnaires, modifiables, ne peuvent pas être des\nclés."
    }
  ]
}