{
  "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`, `items` et `get`. 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",
          "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 indexée par des entiers",
          "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 associant des clés à des valeurs (par exemple un nom à un âge), permettant un accès rapide par clé",
          "correct": true,
          "feedback": "On peut le voir comme un index\nou un annuaire. Un dictionnaire\nest aussi appelé « tableau\nassociatif » dans certains\nlangages.\n"
        },
        {
          "text": "Une simple chaîne de caractères",
          "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\npeut utiliser\n`d.get(\"nom\", default)`, qui\nrenvoie la valeur par défaut\nindiquée (ou `None` par défaut)\nsi la clé n'est pas présente."
    },
    {
      "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` est définie\npour les ensembles (`set`),\nmais pas pour les\ndictionnaires.\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\npaires** 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": [
        "supprimer"
      ],
      "title": "Supprimer une entrée",
      "statement": "Comment **supprime-t-on** la clé\n`\"age\"` d'un dictionnaire `d` ?",
      "options": [
        {
          "text": "Avec `d.delete(\"age\")`",
          "correct": false,
          "feedback": "La méthode `delete` n'existe\npas pour les dictionnaires\nen Python.\n"
        },
        {
          "text": "Avec `d[\"age\"] = None`",
          "correct": false,
          "feedback": "Cette opération met la\nvaleur à `None`, mais la\nclé reste présente dans le\ndictionnaire.\n"
        },
        {
          "text": "Avec `d.remove(\"age\")`",
          "correct": false,
          "feedback": "La méthode `remove` est\ndéfinie pour les listes,\nmais pas pour les\ndictionnaires.\n"
        },
        {
          "text": "Avec `del d[\"age\"]`",
          "correct": true,
          "feedback": "On utilise le mot-clé `del`.\nSi la clé n'existe pas,\nPython lève une exception\n`KeyError`. Pour éviter\nl'erreur, on peut utiliser\n`d.pop(\"age\", None)`, qui\nrenvoie en plus la valeur\nretirée.\n"
        }
      ],
      "explanation": "Distinction importante : mettre\nla valeur à `None` revient à\ndire « la clé existe, mais\navec une valeur nulle » ;\nl'opération `del` supprime\nréellement la clé du\ndictionnaire."
    },
    {
      "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 (la clé) à un ensemble d'informations (la valeur), comme un nom à un âge ou un code postal à une ville",
          "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 des 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": [
        "trace"
      ],
      "title": "Trace simple",
      "statement": "Que produit ce code ?\n\n```python\nd = {\"a\": 1, \"b\": 2}\nd[\"c\"] = 3\nprint(len(d))\n```",
      "options": [
        {
          "text": "Il déclenche une erreur",
          "correct": false,
          "feedback": "Le code est parfaitement\nvalide. Aucune erreur n'est\nlevée.\n"
        },
        {
          "text": "Il affiche `\"a\", \"b\", \"c\"`",
          "correct": false,
          "feedback": "La fonction `len` renvoie\nun entier, pas la liste des\nclés. Pour obtenir les\nclés, on utiliserait\n`list(d.keys())`.\n"
        },
        {
          "text": "Il affiche `3`",
          "correct": true,
          "feedback": "Le dictionnaire contenait\ndeux paires au départ. On\nen ajoute une troisième\n(`\"c\": 3`), donc `len(d)`\nrenvoie $3$.\n"
        },
        {
          "text": "Il affiche `2`",
          "correct": false,
          "feedback": "Cette réponse ignore l'ajout\nde la nouvelle clé `\"c\"`.\n"
        }
      ],
      "explanation": "Vérifier la longueur d'un\ndictionnaire est utile pour\ns'assurer qu'on n'a pas écrasé\npar mégarde une entrée\nexistante en utilisant la\nmême clé."
    },
    {
      "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    c[m] = c.get(m, 0) + 1\n```\n",
          "correct": true,
          "feedback": "C'est un motif classique.\nL'appel `c.get(m, 0)`\nrenvoie $0$ si la clé `m`\nn'est pas encore présente.\nUne alternative plus\nconcise consiste à utiliser\n`Counter(mots)` du module\n`collections`.\n"
        },
        {
          "text": "```python\nc = sum(mots)\n```\n",
          "correct": false,
          "feedback": "La fonction `sum` ne\nfonctionne pas avec une\nliste de chaînes de\ncaractères. Elle attend\ndes nombres.\n"
        },
        {
          "text": "```python\nc = dict(mots)\n```\n",
          "correct": false,
          "feedback": "Cette construction ne\nfonctionne que si `mots`\ncontient des paires\nclé-valeur, et non des\nchaînes simples.\n"
        },
        {
          "text": "```python\nc = mots.count()\n```\n",
          "correct": false,
          "feedback": "La méthode `count` sur une\nliste compte les occurrences\nd'un seul élément précis,\net non de tous les éléments\nen une fois.\n"
        }
      ],
      "explanation": "Ce motif est si fréquent que\nPython propose la classe\n`collections.Counter`, qui\nautomatise le comptage. Par\nexemple,\n`Counter([\"a\", \"b\", \"a\"])`\nrenvoie `Counter({\"a\": 2,\n\"b\": 1})`."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "methode-get"
      ],
      "title": "Méthode get avec valeur par défaut",
      "statement": "Que fait `d.get(\"x\", 0)` ?",
      "options": [
        {
          "text": "Elle renvoie la valeur associée à `\"x\"` si la clé existe, et $0$ sinon (sans lever d'erreur)",
          "correct": true,
          "feedback": "Cette méthode est très\nutile pour éviter une\nexception `KeyError`. Le\nsecond paramètre est la\nvaleur par défaut, à\nrenvoyer en l'absence de\nla clé.\n"
        },
        {
          "text": "Elle renvoie toujours la valeur $0$",
          "correct": false,
          "feedback": "La méthode renvoie $0$\nuniquement quand la clé\n`\"x\"` n'est pas présente.\nSinon, elle renvoie la\nvaleur associée à la clé.\n"
        },
        {
          "text": "Elle supprime la clé `\"x\"` du dictionnaire",
          "correct": false,
          "feedback": "La méthode `get` se contente\nde lire ; elle ne modifie\npas le dictionnaire.\n"
        },
        {
          "text": "Elle déclenche une erreur si la clé n'existe pas",
          "correct": false,
          "feedback": "C'est précisément l'intérêt\nde `get` que de **ne pas**\ndéclencher d'erreur en\nl'absence de la clé.\n"
        }
      ],
      "explanation": "Sans valeur par défaut,\n`d.get(\"x\")` renvoie `None` si\nla clé est absente. Avec une\nvaleur par défaut, elle renvoie\ncette valeur. Très pratique\ndans les motifs\nd'accumulation."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "items"
      ],
      "title": "Méthode items",
      "statement": "Que renvoie l'appel `d.items()` ?",
      "options": [
        {
          "text": "Une liste contenant uniquement les 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)` du dictionnaire",
          "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é d'accès : un accès en $O(1)$ avec un dictionnaire, contre $O(n)$ avec une liste utilisée pour une recherche linéaire",
          "correct": true,
          "feedback": "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 que les listes ne supportent pas les nombres",
          "correct": false,
          "feedback": "Cette affirmation est\nfausse. Les listes\npeuvent contenir tout\ntype de valeurs,\nnotamment des nombres.\n"
        },
        {
          "text": "Pour économiser 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 différence notable",
          "correct": false,
          "feedback": "Il existe au contraire\nune différence majeure\nde performance, comme\nexpliqué dans la bonne\nréponse.\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\nde 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": [
        "trace-modification"
      ],
      "title": "Modification d'une valeur",
      "statement": "```python\nd = {\"a\": 1, \"b\": 2}\nd[\"a\"] = d[\"a\"] + 10\nprint(d)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `{\"a\": 11, \"b\": 2}`",
          "correct": true,
          "feedback": "La valeur initiale de\n`d[\"a\"]` est $1$, à\nlaquelle on ajoute $10$.\nLa nouvelle valeur est\ndonc $11$.\n"
        },
        {
          "text": "Il déclenche une erreur",
          "correct": false,
          "feedback": "Le code est parfaitement\nvalide.\n"
        },
        {
          "text": "Il affiche `{\"a\": 1, \"b\": 2}`",
          "correct": false,
          "feedback": "Cette réponse ignore la\nmodification de `d[\"a\"]`,\nqui passe de $1$ à $11$.\n"
        },
        {
          "text": "Il affiche `{\"a\": 10, \"b\": 2}`",
          "correct": false,
          "feedback": "On ajoute $10$ à la\nvaleur précédente, on ne\nremplace pas la valeur\npar $10$.\n"
        }
      ],
      "explanation": "Une forme abrégée\nidiomatique consiste à\nécrire `d[\"a\"] += 10`,\nplus concise et plus\nlisible."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "defaultdict"
      ],
      "title": "Le type defaultdict",
      "statement": "Que fournit l'instruction\n`from collections import\ndefaultdict` ?",
      "options": [
        {
          "text": "Elle importe une simple variable",
          "correct": false,
          "feedback": "Cette interprétation est\nimprécise. Il s'agit en\nréalité d'un type\nspécialisé, comme\nexpliqué dans la bonne\nréponse.\n"
        },
        {
          "text": "Elle crée un dictionnaire vide",
          "correct": false,
          "feedback": "Le dictionnaire vide se\ncrée avec `{}` ou\n`dict()`. La classe\n`defaultdict` apporte\nune fonctionnalité\nsupplémentaire : la\ncréation automatique de\nvaleurs par défaut.\n"
        },
        {
          "text": "Elle importe un fichier annexe",
          "correct": false,
          "feedback": "Cette instruction concerne\nun type Python, et non\nun fichier sur le\ndisque.\n"
        },
        {
          "text": "Elle importe un type de dictionnaire qui crée automatiquement une valeur par défaut lorsqu'on accède à une clé inexistante (utile pour les comptages ou les regroupements)",
          "correct": true,
          "feedback": "Par exemple, en écrivant\n`c = defaultdict(int)`,\non peut directement\nincrémenter\n`c[mot] += 1` sans\ntester d'abord la\nprésence de la clé.\nC'est très pratique.\n"
        }
      ],
      "explanation": "Le module `collections`\nregroupe plusieurs outils\ntrès utiles : `defaultdict`,\n`Counter`, `OrderedDict`,\n`namedtuple`, et d'autres\nencore."
    },
    {
      "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\nclé**, on écrit\n`for k in sorted(d):`. Pour\nun parcours **trié par\nvaleur**, 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": "q21",
      "difficulty": 3,
      "skills": [
        "hash-collisions"
      ],
      "title": "Hachage et collisions",
      "statement": "Pourquoi l'accès à un\ndictionnaire est-il en\nmoyenne en $O(1)$, mais\nau pire en $O(n)$ ?",
      "options": [
        {
          "text": "Aucune raison particulière",
          "correct": false,
          "feedback": "Cette différence\ns'explique au contraire\npar le fonctionnement\ninterne de la table de\nhachage, comme expliqué\ndans la bonne réponse.\n"
        },
        {
          "text": "Parce que les clés sont triées en interne",
          "correct": false,
          "feedback": "Les clés ne sont pas\ntriées dans une table\nde hachage. C'est\nprécisément ce qui\npermet l'accès en $O(1)$\nen moyenne.\n"
        },
        {
          "text": "Parce que la table de hachage peut subir des collisions (deux clés qui se retrouvent au même emplacement). Au pire (toutes les clés au même emplacement), on retombe sur une recherche linéaire",
          "correct": true,
          "feedback": "Ce phénomène reste\nnéanmoins rare en\npratique. Python utilise\ndes stratégies internes\n(adressage ouvert,\nredimensionnement) qui\nmaintiennent les\ncollisions à un niveau\nacceptable.\n"
        },
        {
          "text": "Parce que les dictionnaires sont mal conçus en Python",
          "correct": false,
          "feedback": "Cette affirmation est\nincorrecte. Les\ndictionnaires Python\nsont au contraire\nréputés pour leur\nexcellente performance.\n"
        }
      ],
      "explanation": "Cette garantie est\nprobabiliste, et non\ndéterministe. Pour des\nsituations adversariales\n(sécurité), Python\nrandomise le hachage des\nchaînes via la variable\nd'environnement\n`PYTHONHASHSEED`."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "setdefault"
      ],
      "title": "La méthode setdefault",
      "statement": "Que fait `d.setdefault(\"k\",\n0)` ?",
      "options": [
        {
          "text": "Elle écrase systématiquement la valeur de la clé `\"k\"` avec $0$",
          "correct": false,
          "feedback": "La méthode n'écrase pas\nla valeur existante.\nElle ne définit la\nvaleur par défaut que si\nla clé n'est pas déjà\nprésente.\n"
        },
        {
          "text": "La méthode `setdefault` n'existe pas en Python",
          "correct": false,
          "feedback": "Cette méthode existe\nbel et bien, et fait\npartie de l'interface\nstandard des\ndictionnaires.\n"
        },
        {
          "text": "Elle déclenche systématiquement une erreur",
          "correct": false,
          "feedback": "La méthode `setdefault`\nne lève pas d'erreur ;\nc'est précisément son\nintérêt par rapport à\nun accès direct.\n"
        },
        {
          "text": "Si la clé `\"k\"` existe, elle renvoie sa valeur. Sinon, elle l'ajoute avec la valeur $0$ et renvoie $0$",
          "correct": true,
          "feedback": "Cette méthode combine\ndeux opérations en une\nseule : elle est utile\npour initialiser une\nclé à la première\nrencontre.\n"
        }
      ],
      "explanation": "Cette méthode est utile\nmais elle est souvent\nremplacée par\n`defaultdict`, plus\nexpressif. Elle reste\npratique surtout quand la\nvaleur par défaut est\ncoûteuse à calculer."
    },
    {
      "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": "Le code s'exécute plus rapidement",
          "correct": false,
          "feedback": "Aucune amélioration de\nperformance n'est\nobtenue par cette\nmodification ; c'est\nau contraire une source\npotentielle de bugs.\n"
        },
        {
          "text": "Cette modification est interdite par la loi",
          "correct": false,
          "feedback": "Cette interprétation\nlittérale n'a aucun sens\nen programmation.\n"
        },
        {
          "text": "Aucune conséquence particulière",
          "correct": false,
          "feedback": "Cette modification a au\ncontraire des\nconséquences sérieuses,\ncomme expliqué dans la\nbonne réponse.\n"
        },
        {
          "text": "Cela peut lever une exception `RuntimeError` indiquant que la taille du dictionnaire a changé pendant l'itération, ou produire des comportements imprévisibles",
          "correct": true,
          "feedback": "Python détecte la\nmodification et lève une\nerreur. 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": "q24",
      "difficulty": 3,
      "skills": [
        "comprehension-dict"
      ],
      "title": "Compréhension de dictionnaire",
      "statement": "Quel code crée le dictionnaire\n`{1: 1, 2: 4, 3: 9, ...,\n10: 100}` (le carré de\nchaque entier) ?",
      "options": [
        {
          "text": "`{i: i  2 for i in range(1, 11)}`",
          "correct": true,
          "feedback": "C'est une **compréhension\nde dictionnaire**. Sa\nsyntaxe est\n`{cle: valeur for ... in\n...}`. Cette construction\nest concise et\nidiomatique en Python.\n"
        },
        {
          "text": "`{i  2 for i in range(1, 11)}`",
          "correct": false,
          "feedback": "Cette syntaxe crée un\n**ensemble**, et non un\ndictionnaire, car il\nn'y a pas de paires\nclé-valeur.\n"
        },
        {
          "text": "`[i: i  2 for i in range(1, 11)]`",
          "correct": false,
          "feedback": "Cette syntaxe est\ninvalide en Python. Les\ncrochets ne permettent\npas de définir une\ncompréhension avec des\npaires clé-valeur.\n"
        },
        {
          "text": "`dict(i, i  2 for i in range(1, 11))`",
          "correct": false,
          "feedback": "Cette syntaxe est\ninvalide. La fonction\n`dict` ne s'utilise pas\nde cette manière.\n"
        }
      ],
      "explanation": "Quatre formes de\ncompréhension existent en\nPython : `[...]` pour les\nlistes, `{x for ...}` pour\nles ensembles,\n`{k: v for ...}` pour les\ndictionnaires, et\n`(... for ...)` pour les\ngénérateurs."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "statement": "Parmi les affirmations\nsuivantes sur les\ndictionnaires, laquelle est\n**fausse** ?",
      "options": [
        {
          "text": "La méthode `d.get(k, default)` renvoie `default` si la clé est absente, sans lever d'erreur",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte. C'est\nprécisément l'intérêt\nde cette méthode par\nrapport à un accès\ndirect.\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",
        "comprehension"
      ],
      "title": "Inverser un dictionnaire",
      "statement": "On souhaite, à partir d'un dictionnaire `d` aux\nvaleurs distinctes, obtenir un nouveau dictionnaire\noù les clés et les valeurs sont échangées. Quelle\ncompréhension convient ?",
      "options": [
        {
          "text": "`{v: k for k in d}`",
          "correct": false,
          "feedback": "Erreur : sans appeler `d.items()`, la variable\n`v` n'est pas définie. Cette syntaxe ne\nfonctionne qu'avec `for k in d`, où `k` est\nune clé, pas un couple.\n"
        },
        {
          "text": "`{d[k]: k for k in d.values()}`",
          "correct": false,
          "feedback": "Erreur : la boucle parcourt les **valeurs** de\n`d`, pas ses clés. L'expression `d[k]` est donc\nincorrecte (les valeurs ne sont pas des clés\ndu dictionnaire).\n"
        },
        {
          "text": "`{v: k for k, v in d.items()}`",
          "correct": true,
          "feedback": "Bonne réponse : la méthode `items()` renvoie une\nséquence de couples `(clé, valeur)`. Le\ndéballage `for k, v in d.items()` permet ensuite\nd'écrire le couple inversé `v: k` dans le\nnouveau dictionnaire.\n"
        },
        {
          "text": "`{k: v for v, k in d.items()}`",
          "correct": false,
          "feedback": "Erreur : on a inversé l'ordre des variables\ndans le déballage, mais on a oublié de\nréécrire la paire `v: k` à gauche. Le résultat\nne sera pas inversé : on retrouve le\ndictionnaire d'origine.\n"
        }
      ],
      "explanation": "Cette technique ne fonctionne correctement que si\nles valeurs sont uniques. Si plusieurs clés\npartagent une même valeur, la conversion\n« écrasera » les doublons et ne conservera qu'une\ndes associations possibles. 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 un\nitérable 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 le résultat\nd'un `zip` sur deux listes).\n"
        },
        {
          "text": "`{'rouge', 'vert', 'bleu'}`",
          "correct": false,
          "feedback": "Erreur : ce serait un **ensemble** (`set`).\n`dict` produit un dictionnaire avec des clés\n**et** des valeurs ; il ne se limite pas aux\nclé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": "Trois façons usuelles de créer un dictionnaire :\nlittéral `{\"a\": 1, \"b\": 2}`, fonction\n`dict(a=1, b=2)` (clés en chaînes implicites) et\n`dict(zip(cles, valeurs))` (depuis deux listes\nparallèles). La forme « liste de couples » est très\nutile lors de la lecture de fichiers structurés."
    }
  ]
}