{
  "chapter": {
    "id": "tuples-listes",
    "level": "premiere",
    "theme": "Programmation",
    "title": "Tuples et listes",
    "description": "Types construits : tuples (immuables) et listes\n(mutables), création, accès par indice, parcours,\najout avec `append`, longueur avec `len`,\nappartenance avec `in`, compréhensions, conversions\nd'un type à l'autre, affectation multiple et\ndéballage, le choix de la structure selon\nl'usage, et les tranches (slicing) : bornes,\nindices négatifs, pas et copie.",
    "parties": [
      {
        "id": "listes",
        "title": "Listes"
      },
      {
        "id": "tuples",
        "title": "Tuples"
      },
      {
        "id": "choisir",
        "title": "Choisir entre liste et tuple"
      },
      {
        "id": "slicing",
        "title": "Slicing"
      }
    ],
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition-liste"
      ],
      "title": "Création d'une liste",
      "partie": "listes",
      "statement": "Comment crée-t-on une **liste** vide en\nPython ?",
      "options": [
        {
          "text": "Avec la syntaxe `liste = []`",
          "correct": true,
          "feedback": "On utilise les crochets pour\ndélimiter une liste. Une forme\néquivalente consiste à écrire\n`liste = list()`.\n"
        },
        {
          "text": "Avec la syntaxe `liste = ()`",
          "correct": false,
          "feedback": "Cette syntaxe crée un **tuple**\nvide, et non une liste.\n"
        },
        {
          "text": "Avec la syntaxe `liste = {}`",
          "correct": false,
          "feedback": "Les accolades sont utilisées\npour les **dictionnaires** ou\nles ensembles, et non pour les\nlistes.\n"
        },
        {
          "text": "Avec la syntaxe `liste = null`",
          "correct": false,
          "feedback": "Le mot-clé `null` n'existe pas\nen Python ; l'équivalent\ns'appelle `None`, qui ne\nreprésente d'ailleurs pas une\nliste.\n"
        }
      ],
      "explanation": "Trois conteneurs natifs principaux\nsont à distinguer : les listes\n(avec `[]`), les tuples (avec\n`()`) et les dictionnaires (avec\n`{}`). Ils diffèrent par leur\nmutabilité, leur ordre et leur\nmode d'accès."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "tuple"
      ],
      "title": "Création d'un tuple",
      "partie": "tuples",
      "statement": "Comment crée-t-on un **tuple** en\nPython ?",
      "options": [
        {
          "text": "Avec `t = tuple[1, 2, 3]`, crochets après le mot `tuple`",
          "correct": false,
          "feedback": "Erreur : `tuple[...]` avec des crochets\nest une annotation de type, pas une\ncréation de tuple. Pour convertir une\nliste, on écrit `tuple([1, 2, 3])`.\n"
        },
        {
          "text": "Avec `t = (1, 2, 3)`, ou même `t = 1, 2, 3`",
          "correct": true,
          "feedback": "Bonne réponse : les parenthèses sont\nsouvent facultatives ; ce sont les\nvirgules qui définissent un tuple. Le\ntuple vide s'écrit `()` et, pour un\nseul élément, la virgule est\nobligatoire : `t = (1,)`.\n"
        },
        {
          "text": "Avec `t = {1, 2, 3}`, les accolades regroupant les éléments",
          "correct": false,
          "feedback": "Erreur : les accolades autour de\nplusieurs éléments séparés par des\nvirgules créent un **ensemble**, un\nautre type de collection qui n'est\npas au programme de première, et\nnon un tuple.\n"
        },
        {
          "text": "Avec `t = [1, 2, 3]`, puis `t.freeze()` pour le figer",
          "correct": false,
          "feedback": "Erreur : les crochets créent une liste,\net aucune méthode `freeze` n'existe. La\nconversion se fait avec `tuple(liste)`.\n"
        }
      ],
      "explanation": "Une distinction subtile : `(1)` est\nsimplement l'entier $1$ entouré\nde parenthèses de groupement,\ntandis que `(1,)` est un tuple à\nun seul élément. C'est la\nvirgule, et non les parenthèses,\nqui crée le tuple."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "mutabilite"
      ],
      "title": "Mutabilité",
      "partie": "choisir",
      "statement": "Quelle est la **différence fondamentale** entre un tuple et\nune liste ?",
      "options": [
        {
          "text": "Un tuple contient forcément exactement deux éléments",
          "correct": false,
          "feedback": "Aucune contrainte sur le\nnombre d'éléments : un tuple\npeut en contenir zéro, un, ou\nbeaucoup.\n"
        },
        {
          "text": "Aucune différence, seule la notation change",
          "correct": false,
          "feedback": "Erreur : la notation diffère, mais\nsurtout le comportement : une liste se\nmodifie sur place, un tuple jamais.\n"
        },
        {
          "text": "La liste est modifiable, le tuple ne l'est pas",
          "correct": true,
          "feedback": "Bonne réponse : une liste est\n**mutable**, un tuple **immuable**\n(figé). En conséquence, les tuples\npeuvent être utilisés comme\nclés de dictionnaire, ce que\nles listes ne peuvent pas\nfaire. Les tuples sont\négalement un peu plus rapides\nà manipuler.\n"
        },
        {
          "text": "Un tuple ne contient que des nombres, une liste tout type",
          "correct": false,
          "feedback": "Erreur : un tuple accepte n'importe quels\nobjets, comme une liste (`(\"a\", 1, [2])`\nest valide). La différence concerne la\nmutabilité.\n"
        }
      ],
      "explanation": "La mutabilité désigne la\npossibilité d'être modifié.\nL'instruction `liste.append(x)`\nmodifie la liste sur place. Sur\nun tuple, l'appel\n`tuple.append(x)` provoque une\nexception `AttributeError`."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "acces-indice"
      ],
      "title": "Accès par indice",
      "partie": "listes",
      "statement": "Que renvoie l'expression\n`[\"a\", \"b\", \"c\"][1]` ?",
      "options": [
        {
          "text": "La chaîne `\"c\"`",
          "correct": false,
          "feedback": "La chaîne `\"c\"` se trouve à\nl'indice $2$, et non à\nl'indice $1$.\n"
        },
        {
          "text": "La chaîne `\"b\"`",
          "correct": true,
          "feedback": "L'indice $1$ correspond au\ndeuxième élément de la liste.\nC'est la convention standard\ndes langages indexés à partir\nde $0$.\n"
        },
        {
          "text": "La chaîne `\"a\"`",
          "correct": false,
          "feedback": "En Python, l'indexation\ncommence à $0$. L'élément\nd'indice $0$ est donc `\"a\"`,\net l'élément d'indice $1$ est\n`\"b\"`.\n"
        },
        {
          "text": "Une exception est levée",
          "correct": false,
          "feedback": "L'indice $1$ est valide pour\nune liste de trois éléments :\naucune exception n'est levée.\n"
        }
      ],
      "explanation": "Les indices d'une liste de $n$\néléments vont de $0$ à $n - 1$ :\nle premier élément a l'indice $0$\net le dernier l'indice\n`len(liste) - 1`. Un indice\nsupérieur ou égal à $n$ lève une\nexception `IndexError`."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "longueur"
      ],
      "title": "Longueur d'une séquence",
      "partie": "listes",
      "statement": "Comment obtient-on la **longueur**\nd'une liste ou d'un tuple ?",
      "options": [
        {
          "text": "Avec `liste.size()`",
          "correct": false,
          "feedback": "La méthode `size` n'existe\npas en Python. Elle vient du\nmonde C++.\n"
        },
        {
          "text": "Avec `liste.length`",
          "correct": false,
          "feedback": "L'attribut `length` n'existe\npas en Python. Cette syntaxe\nprovient de JavaScript ou de\nJava.\n"
        },
        {
          "text": "Avec `len(liste)`",
          "correct": true,
          "feedback": "La fonction native `len`\nfonctionne sur tous les\nconteneurs standard : listes,\ntuples, chaînes,\ndictionnaires, ensembles.\n"
        },
        {
          "text": "Avec `size(liste)`",
          "correct": false,
          "feedback": "Aucune fonction `size`\nn'existe dans la\nbibliothèque standard de\nPython.\n"
        }
      ],
      "explanation": "La fonction `len` s'exécute en\n$O(1)$, car la longueur est\nstockée à part. L'expression\n`len([])` vaut $0$, ce qui\npermet de tester rapidement si\nune liste est vide."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "append"
      ],
      "title": "Ajouter à une liste",
      "partie": "listes",
      "statement": "Comment **ajoute-t-on** l'élément\n$42$ à la fin d'une liste `liste` ?",
      "options": [
        {
          "text": "Avec `liste += 42`",
          "correct": false,
          "feedback": "L'opérateur `+=` attend un\nitérable, pas un entier.\nL'écriture `liste += [42]`\nfonctionnerait, mais reste\nmoins idiomatique\nqu'`append`.\n"
        },
        {
          "text": "Avec `liste = liste + 42`",
          "correct": false,
          "feedback": "L'opérateur `+` ne sait pas\ncombiner une liste avec un\nentier ; il déclenche une\nexception `TypeError`.\n"
        },
        {
          "text": "Avec `liste.append(42)`",
          "correct": true,
          "feedback": "Cette méthode modifie la\nliste sur place. Par\nexemple, après\n`liste = [1, 2]; liste.append(3)`,\nla liste vaut `[1, 2, 3]`.\n"
        },
        {
          "text": "Avec `liste.add(42)`",
          "correct": false,
          "feedback": "La méthode `add` est définie\npour les ensembles, mais pas\npour les listes.\n"
        }
      ],
      "explanation": "Différence importante : la\nméthode `append` modifie la\nliste sur place, alors que\nl'opérateur `+` crée une\nnouvelle liste. Pour ajouter\nplusieurs éléments d'un coup, on\nécrit `liste = liste + [4, 5, 6]`."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "parcours"
      ],
      "title": "Parcours d'une liste",
      "partie": "listes",
      "statement": "Comment **parcourt-on** tous les\néléments d'une liste pour les\nafficher ?",
      "options": [
        {
          "text": "```python\nfor i in liste.length:\n    print(liste[i])\n```\n",
          "correct": false,
          "feedback": "L'attribut `length` n'existe\npas en Python, et\n`for i in nombre:` n'est pas\nune syntaxe valide.\n"
        },
        {
          "text": "```python\nprint liste\n```\n",
          "correct": false,
          "feedback": "Cette syntaxe correspond à\nPython 2. En Python 3,\n`print` est une fonction et\nrequiert des parenthèses.\n"
        },
        {
          "text": "```python\nfor x in liste:\n    print(x)\n```\n",
          "correct": true,
          "feedback": "C'est la forme idiomatique\nen Python. La variable `x`\nreçoit successivement chacun\ndes éléments de la liste.\n"
        },
        {
          "text": "```python\nwhile liste:\n    print(liste)\n```\n",
          "correct": false,
          "feedback": "Cette boucle est infinie tant\nqu'on ne retire pas des\néléments à la liste, et elle\nn'affiche pas les éléments\nun par un.\n"
        }
      ],
      "explanation": "Pour parcourir une liste avec un\nindice, on écrit\n`for i in range(len(liste)):`.\nOn dispose alors à la fois de\nl'indice `i` et de la valeur\n`liste[i]` à chaque tour de boucle."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "in-operator"
      ],
      "title": "Test d'appartenance",
      "partie": "listes",
      "statement": "Comment teste-t-on si l'élément\n$42$ est présent dans une liste\n`liste` ?",
      "options": [
        {
          "text": "Avec `liste.has(42)`",
          "correct": false,
          "feedback": "Aucune méthode de ce nom\nn'existe pour les listes en\nPython.\n"
        },
        {
          "text": "Avec `42 in liste`",
          "correct": true,
          "feedback": "L'opérateur `in` renvoie un booléen indiquant la présence ou\nl'absence de l'élément. Sur une liste, Python parcourt les éléments\nun par un jusqu'à trouver $42$.\n"
        },
        {
          "text": "Avec `liste.find(42)`",
          "correct": false,
          "feedback": "La méthode `find` est définie\npour les chaînes de\ncaractères, mais pas pour les\nlistes.\n"
        },
        {
          "text": "Avec `liste.contains(42)`",
          "correct": false,
          "feedback": "La méthode `contains`\nn'existe pas en Python.\n"
        }
      ],
      "explanation": "On peut aussi écrire ce test soi-même : une boucle `for` parcourt\nla liste et compare chaque élément à $42$. Le coût est linéaire\ndans les deux cas."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "comparaison-tuple-liste"
      ],
      "title": "Quand utiliser un tuple ?",
      "partie": "choisir",
      "statement": "Dans quel cas préfère-t-on un\n**tuple** à une liste ?",
      "options": [
        {
          "text": "Uniquement pour stocker des valeurs numériques entières",
          "correct": false,
          "feedback": "Aucune restriction sur le\ntype des éléments. Un tuple\npeut contenir des chaînes,\ndes objets, ou tout autre\ntype.\n"
        },
        {
          "text": "Pour des données qui ne doivent pas être modifiées (constantes, coordonnées)",
          "correct": true,
          "feedback": "Le tuple offre la sécurité\n(l'immuabilité empêche les\nmodifications\naccidentelles), une légère\nefficacité supplémentaire,\net permet l'usage comme clé\nde dictionnaire.\n"
        },
        {
          "text": "Pour économiser quelques touches au clavier",
          "correct": false,
          "feedback": "Cette motivation est trop\nsuperficielle. Le choix\nentre tuple et liste est\nd'abord d'ordre conceptuel.\n"
        },
        {
          "text": "Pour stocker des données qui changent souvent au cours du programme",
          "correct": false,
          "feedback": "Pour des données dynamiques,\nla liste est plus adaptée\npuisqu'elle est mutable.\n"
        }
      ],
      "explanation": "Cas d'usage typiques d'un tuple :\ncoordonnées comme `(x, y)`,\nretours multiples d'une fonction\navec `return a, b`, ou\nenregistrement comme\n`(\"Alice\", 17, \"Lyon\")`."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "exemple-simple"
      ],
      "title": "Trace simple",
      "partie": "listes",
      "statement": "Que produit ce code ?\n```python\nl = [1, 2, 3]\nl.append(4)\nprint(len(l))\n```",
      "options": [
        {
          "text": "Il affiche `5`",
          "correct": false,
          "feedback": "On ajoute un seul élément\nà une liste qui en contenait\n3 : la longueur passe\nde $3$ à $4$, et non à $5$.\n"
        },
        {
          "text": "Il affiche `3`",
          "correct": false,
          "feedback": "Cette réponse ignore l'ajout\nde l'élément $4$ par la\nméthode `append`.\n"
        },
        {
          "text": "Il affiche `4`",
          "correct": true,
          "feedback": "La liste initiale est\n`[1, 2, 3]`. Après\n`l.append(4)`, elle vaut\n`[1, 2, 3, 4]`, donc sa\nlongueur est $4$.\n"
        },
        {
          "text": "Il déclenche une exception",
          "correct": false,
          "feedback": "Le code est parfaitement\nvalide.\n"
        }
      ],
      "explanation": "La méthode `append` modifie la\nliste sur place. Il ne faut pas\nécrire `l = l.append(4)` :\n`append` ne renvoie rien (la\nvaleur renvoyée est `None`),\ndonc `l` se retrouverait à\n`None`.",
      "variants": [
        {
          "statement": "Que produit ce code ?\n```python\nl = [1, 2, 3]\nl.append(4)\nprint(len(l))\n```",
          "options": [
            {
              "text": "Il affiche `5`",
              "correct": false,
              "feedback": "On ajoute un seul élément\nà une liste qui en contenait\n3 : la longueur passe\nde $3$ à $4$, et non à $5$.\n"
            },
            {
              "text": "Il affiche `3`",
              "correct": false,
              "feedback": "Cette réponse ignore l'ajout\nde l'élément $4$ par la\nméthode `append`.\n"
            },
            {
              "text": "Il affiche `4`",
              "correct": true,
              "feedback": "La liste initiale est\n`[1, 2, 3]`. Après\n`l.append(4)`, elle vaut\n`[1, 2, 3, 4]`, donc sa\nlongueur est $4$.\n"
            },
            {
              "text": "Il déclenche une exception",
              "correct": false,
              "feedback": "Le code est parfaitement\nvalide.\n"
            }
          ],
          "explanation": "La méthode `append` modifie la\nliste sur place. Il ne faut pas\nécrire `l = l.append(4)` :\n`append` ne renvoie rien (la\nvaleur renvoyée est `None`),\ndonc `l` se retrouverait à\n`None`."
        },
        {
          "statement": "Que produit ce code ?\n```python\nl = [5, 6]\nl.append(7)\nprint(len(l))\n```",
          "options": [
            {
              "text": "Il affiche `4`",
              "correct": false,
              "feedback": "On ajoute un seul élément\nà une liste qui en contenait\n2 : la longueur passe\nde $2$ à $3$, et non à $4$.\n"
            },
            {
              "text": "Il affiche `2`",
              "correct": false,
              "feedback": "Cette réponse ignore l'ajout\nde l'élément $7$ par la\nméthode `append`.\n"
            },
            {
              "text": "Il affiche `3`",
              "correct": true,
              "feedback": "La liste initiale est\n`[5, 6]`. Après\n`l.append(7)`, elle vaut\n`[5, 6, 7]`, donc sa\nlongueur est $3$.\n"
            },
            {
              "text": "Il déclenche une exception",
              "correct": false,
              "feedback": "Le code est parfaitement\nvalide.\n"
            }
          ],
          "explanation": "La méthode `append` modifie la\nliste sur place. Il ne faut pas\nécrire `l = l.append(7)` :\n`append` ne renvoie rien (la\nvaleur renvoyée est `None`),\ndonc `l` se retrouverait à\n`None`."
        },
        {
          "statement": "Que produit ce code ?\n```python\nl = [9, 8, 7, 6]\nl.append(1)\nprint(len(l))\n```",
          "options": [
            {
              "text": "Il affiche `6`",
              "correct": false,
              "feedback": "On ajoute un seul élément\nà une liste qui en contenait\n4 : la longueur passe\nde $4$ à $5$, et non à $6$.\n"
            },
            {
              "text": "Il affiche `4`",
              "correct": false,
              "feedback": "Cette réponse ignore l'ajout\nde l'élément $1$ par la\nméthode `append`.\n"
            },
            {
              "text": "Il affiche `5`",
              "correct": true,
              "feedback": "La liste initiale est\n`[9, 8, 7, 6]`. Après\n`l.append(1)`, elle vaut\n`[9, 8, 7, 6, 1]`, donc sa\nlongueur est $5$.\n"
            },
            {
              "text": "Il déclenche une exception",
              "correct": false,
              "feedback": "Le code est parfaitement\nvalide.\n"
            }
          ],
          "explanation": "La méthode `append` modifie la\nliste sur place. Il ne faut pas\nécrire `l = l.append(1)` :\n`append` ne renvoie rien (la\nvaleur renvoyée est `None`),\ndonc `l` se retrouverait à\n`None`."
        }
      ]
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "comprehension",
        "range",
        "acces-indice"
      ],
      "title": "Extraire des éléments consécutifs",
      "partie": "listes",
      "statement": "On dispose de `t = [10, 20, 30, 40, 50]`. Que vaut\nl'expression `[t[i] for i in range(1, 4)]` ?",
      "options": [
        {
          "text": "La liste `[10, 20, 30]`",
          "correct": false,
          "feedback": "Les indices commencent à $0$ :\n`t[1]` désigne le deuxième\nélément, $20$, et non celui qui\nle précède.\n"
        },
        {
          "text": "La liste `[20, 30, 40]`",
          "correct": true,
          "feedback": "`range(1, 4)` produit les indices\n$1$, $2$ et $3$, la borne $4$ étant\nexclue. La compréhension prend\nles éléments situés à ces\nindices : $20$, $30$ et $40$.\n"
        },
        {
          "text": "La liste `[1, 2, 3]`",
          "correct": false,
          "feedback": "Ce sont les indices produits\npar `range(1, 4)`. La\ncompréhension ne garde pas `i`\nmais `t[i]`, l'élément situé à\ncet indice.\n"
        },
        {
          "text": "La liste `[20, 30, 40, 50]`",
          "correct": false,
          "feedback": "La borne haute de `range` est\n**exclue** : l'indice $4$, qui\ndésigne $50$, n'est pas\nproduit.\n"
        }
      ],
      "explanation": "Dans `range(a, b)`, la borne `a`\nest incluse et la borne `b`\nexclue. La compréhension\n`[t[i] for i in range(a, b)]`\nconstruit donc la liste des\néléments d'indices `a` à `b - 1`.",
      "variants": [
        {
          "statement": "On dispose de `t = [10, 20, 30, 40, 50]`. Que vaut\nl'expression `[t[i] for i in range(1, 4)]` ?",
          "options": [
            {
              "text": "La liste `[10, 20, 30]`",
              "correct": false,
              "feedback": "Les indices commencent à $0$ :\n`t[1]` désigne le deuxième\nélément, $20$, et non celui qui\nle précède.\n"
            },
            {
              "text": "La liste `[20, 30, 40]`",
              "correct": true,
              "feedback": "`range(1, 4)` produit les indices\n$1$, $2$ et $3$, la borne $4$ étant\nexclue. La compréhension prend\nles éléments situés à ces\nindices : $20$, $30$ et $40$.\n"
            },
            {
              "text": "La liste `[1, 2, 3]`",
              "correct": false,
              "feedback": "Ce sont les indices produits\npar `range(1, 4)`. La\ncompréhension ne garde pas `i`\nmais `t[i]`, l'élément situé à\ncet indice.\n"
            },
            {
              "text": "La liste `[20, 30, 40, 50]`",
              "correct": false,
              "feedback": "La borne haute de `range` est\n**exclue** : l'indice $4$, qui\ndésigne $50$, n'est pas\nproduit.\n"
            }
          ],
          "explanation": "Dans `range(a, b)`, la borne `a`\nest incluse et la borne `b`\nexclue. La compréhension\n`[t[i] for i in range(a, b)]`\nconstruit donc la liste des\néléments d'indices `a` à `b - 1`."
        },
        {
          "statement": "On dispose de `t = [5, 6, 7, 8, 9]`. Que vaut\nl'expression `[t[i] for i in range(2, 4)]` ?",
          "options": [
            {
              "text": "La liste `[6, 7]`",
              "correct": false,
              "feedback": "Les indices commencent à $0$ :\n`t[2]` désigne le troisième\nélément, $7$, et non celui qui\nle précède.\n"
            },
            {
              "text": "La liste `[7, 8]`",
              "correct": true,
              "feedback": "`range(2, 4)` produit les indices\n$2$ et $3$, la borne $4$ étant\nexclue. La compréhension prend\nles éléments situés à ces\nindices : $7$ et $8$.\n"
            },
            {
              "text": "La liste `[2, 3]`",
              "correct": false,
              "feedback": "Ce sont les indices produits\npar `range(2, 4)`. La\ncompréhension ne garde pas `i`\nmais `t[i]`, l'élément situé à\ncet indice.\n"
            },
            {
              "text": "La liste `[7, 8, 9]`",
              "correct": false,
              "feedback": "La borne haute de `range` est\n**exclue** : l'indice $4$, qui\ndésigne $9$, n'est pas\nproduit.\n"
            }
          ],
          "explanation": "Dans `range(a, b)`, la borne `a`\nest incluse et la borne `b`\nexclue. La compréhension\n`[t[i] for i in range(a, b)]`\nconstruit donc la liste des\néléments d'indices `a` à `b - 1`."
        },
        {
          "statement": "On dispose de `t = [3, 1, 4, 1, 5, 9]`. Que vaut\nl'expression `[t[i] for i in range(1, 5)]` ?",
          "options": [
            {
              "text": "La liste `[3, 1, 4, 1]`",
              "correct": false,
              "feedback": "Les indices commencent à $0$ :\n`t[1]` désigne le deuxième\nélément, $1$, et non celui qui\nle précède.\n"
            },
            {
              "text": "La liste `[1, 4, 1, 5]`",
              "correct": true,
              "feedback": "`range(1, 5)` produit les indices\n$1$, $2$, $3$ et $4$, la borne $5$ étant\nexclue. La compréhension prend\nles éléments situés à ces\nindices : $1$, $4$, $1$ et $5$.\n"
            },
            {
              "text": "La liste `[1, 2, 3, 4]`",
              "correct": false,
              "feedback": "Ce sont les indices produits\npar `range(1, 5)`. La\ncompréhension ne garde pas `i`\nmais `t[i]`, l'élément situé à\ncet indice.\n"
            },
            {
              "text": "La liste `[1, 4, 1, 5, 9]`",
              "correct": false,
              "feedback": "La borne haute de `range` est\n**exclue** : l'indice $5$, qui\ndésigne $9$, n'est pas\nproduit.\n"
            }
          ],
          "explanation": "Dans `range(a, b)`, la borne `a`\nest incluse et la borne `b`\nexclue. La compréhension\n`[t[i] for i in range(a, b)]`\nconstruit donc la liste des\néléments d'indices `a` à `b - 1`."
        }
      ]
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "unpacking"
      ],
      "title": "Affectation multiple",
      "partie": "tuples",
      "statement": "Quelles sont les valeurs de `a`,\n`b` et `c` après l'instruction\n`(a, b, c) = (1, 2, 3)` ?",
      "options": [
        {
          "text": "Le tuple `((1, 2, 3),)`",
          "correct": false,
          "feedback": "Aucune imbrication ne se\nproduit ici. Chaque variable\nreçoit une seule valeur.\n"
        },
        {
          "text": "$a = 1$, $b = 1$, $c = 1$",
          "correct": false,
          "feedback": "L'affectation multiple\nattribue chaque valeur du\ntuple de droite à la variable\ncorrespondante de gauche, et\nnon la même valeur à toutes.\n"
        },
        {
          "text": "$a = 1$, $b = 2$, $c = 3$",
          "correct": true,
          "feedback": "C'est ce qu'on appelle le\n**dépaquetage** d'un tuple.\nLes parenthèses peuvent\nêtre omises :\n`a, b, c = 1, 2, 3`. Cette\nécriture est très utile,\nnotamment pour échanger\ndeux variables :\n`a, b = b, a`.\n"
        },
        {
          "text": "Une exception est levée",
          "correct": false,
          "feedback": "Le code est parfaitement\nvalide.\n"
        }
      ],
      "explanation": "L'affectation multiple sert aussi à récupérer d'un coup les valeurs\nd'un p-uplet renvoyé par une fonction : si `f()` renvoie `(4, 7)`,\nl'instruction `x, y = f()` affecte $x = 4$ et $y = 7$."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "comprehension",
        "dernier-element"
      ],
      "title": "Retirer le dernier élément",
      "partie": "listes",
      "statement": "On dispose de `l = [10, 20, 30]`. Quelle expression\nfournit une **nouvelle** liste contenant tous les\néléments de `l` sauf le dernier, quelle que soit la\nlongueur de `l` ?",
      "options": [
        {
          "text": "`[l[i] for i in range(1, len(l))]`",
          "correct": false,
          "feedback": "Erreur : les indices vont de $1$ à\n`len(l) - 1`, ce qui retire le **premier**\nélément. Sur l'exemple, on obtient\n`[20, 30]`.\n"
        },
        {
          "text": "`[l[i] for i in range(len(l) - 1)]`",
          "correct": true,
          "feedback": "Bonne réponse : `range(len(l) - 1)` produit\nles indices de $0$ à `len(l) - 2`, soit tous\nsauf celui du dernier élément. On obtient\n`[10, 20]` dans une nouvelle liste ; `l` n'est\npas modifiée.\n"
        },
        {
          "text": "`[l[i] for i in range(len(l) - 2)]`",
          "correct": false,
          "feedback": "Erreur : la borne `len(l) - 2` étant exclue,\nles indices s'arrêtent à `len(l) - 3` et les\n**deux** derniers éléments disparaissent. Sur\nl'exemple, on obtient `[10]`.\n"
        },
        {
          "text": "`l[len(l) - 1]`",
          "correct": false,
          "feedback": "Erreur : sans compréhension, les crochets font\nun accès par indice. `l[len(l) - 1]` est le\ndernier élément lui-même, ici $30$, et non une\nliste.\n"
        }
      ],
      "explanation": "Dans la compréhension `[l[i] for i in range(n)]`,\nles indices vont de $0$ à `n - 1`. En prenant\n`len(l) - 1` comme borne, on s'arrête juste avant\nle dernier élément. Le résultat est une nouvelle\nliste, la liste d'origine reste intacte."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "p-uplet",
        "fonction",
        "retour"
      ],
      "title": "Une fonction qui renvoie deux notes à la fois",
      "partie": "tuples",
      "statement": "```python\ndef bornes(notes):\n    mini = notes[0]\n    maxi = notes[0]\n    for n in notes:\n        if n < mini:\n            mini = n\n        if n > maxi:\n            maxi = n\n    return (mini, maxi)\nresultat = bornes([12, 8, 15, 10])\n```\nQue contient la variable `resultat` après l'exécution de ce code ?",
      "options": [
        {
          "text": "Le tuple `(8, 15)`",
          "correct": true,
          "feedback": "Bonne réponse : la boucle retient la plus petite note, $8$, et la\nplus grande, $15$. L'instruction `return (mini, maxi)` renvoie un\np-uplet de deux valeurs, qui est affecté à `resultat`.\n"
        },
        {
          "text": "La liste `[8, 15]`",
          "correct": false,
          "feedback": "Erreur : les parenthèses de `return (mini, maxi)` construisent un\ntuple, pas une liste. Les valeurs sont justes, mais le type ne\nl'est pas.\n"
        },
        {
          "text": "L'entier `8`",
          "correct": false,
          "feedback": "Erreur : `return` ne s'arrête pas à la première valeur. Il renvoie\nle p-uplet entier `(mini, maxi)`, qui contient les deux notes.\n"
        },
        {
          "text": "Le tuple `(15, 8)`",
          "correct": false,
          "feedback": "Erreur : l'ordre des valeurs est celui écrit dans\n`return (mini, maxi)` : d'abord le minimum, puis le maximum.\n"
        }
      ],
      "explanation": "Une fonction renvoie une seule valeur, mais cette valeur peut être\nun p-uplet : c'est ainsi qu'on renvoie plusieurs résultats à la\nfois. On les récupère ensuite par `resultat[0]` et `resultat[1]`,\nou directement avec `bas, haut = bornes([12, 8, 15, 10])`.",
      "variants": [
        {
          "statement": "```python\ndef bornes(notes):\n    mini = notes[0]\n    maxi = notes[0]\n    for n in notes:\n        if n < mini:\n            mini = n\n        if n > maxi:\n            maxi = n\n    return (mini, maxi)\nresultat = bornes([12, 8, 15, 10])\n```\nQue contient la variable `resultat` après l'exécution de ce code ?",
          "options": [
            {
              "text": "Le tuple `(8, 15)`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle retient la plus petite note, $8$, et la\nplus grande, $15$. L'instruction `return (mini, maxi)` renvoie un\np-uplet de deux valeurs, qui est affecté à `resultat`.\n"
            },
            {
              "text": "La liste `[8, 15]`",
              "correct": false,
              "feedback": "Erreur : les parenthèses de `return (mini, maxi)` construisent un\ntuple, pas une liste. Les valeurs sont justes, mais le type ne\nl'est pas.\n"
            },
            {
              "text": "L'entier `8`",
              "correct": false,
              "feedback": "Erreur : `return` ne s'arrête pas à la première valeur. Il renvoie\nle p-uplet entier `(mini, maxi)`, qui contient les deux notes.\n"
            },
            {
              "text": "Le tuple `(15, 8)`",
              "correct": false,
              "feedback": "Erreur : l'ordre des valeurs est celui écrit dans\n`return (mini, maxi)` : d'abord le minimum, puis le maximum.\n"
            }
          ],
          "explanation": "Une fonction renvoie une seule valeur, mais cette valeur peut être\nun p-uplet : c'est ainsi qu'on renvoie plusieurs résultats à la\nfois. On les récupère ensuite par `resultat[0]` et `resultat[1]`,\nou directement avec `bas, haut = bornes([12, 8, 15, 10])`."
        },
        {
          "statement": "```python\ndef bornes(notes):\n    mini = notes[0]\n    maxi = notes[0]\n    for n in notes:\n        if n < mini:\n            mini = n\n        if n > maxi:\n            maxi = n\n    return (mini, maxi)\nresultat = bornes([7, 14, 9, 18, 11])\n```\nQue contient la variable `resultat` après l'exécution de ce code ?",
          "options": [
            {
              "text": "Le tuple `(7, 18)`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle retient la plus petite note, $7$, et la\nplus grande, $18$. L'instruction `return (mini, maxi)` renvoie un\np-uplet de deux valeurs, qui est affecté à `resultat`.\n"
            },
            {
              "text": "La liste `[7, 18]`",
              "correct": false,
              "feedback": "Erreur : les parenthèses de `return (mini, maxi)` construisent un\ntuple, pas une liste. Les valeurs sont justes, mais le type ne\nl'est pas.\n"
            },
            {
              "text": "L'entier `7`",
              "correct": false,
              "feedback": "Erreur : `return` ne s'arrête pas à la première valeur. Il renvoie\nle p-uplet entier `(mini, maxi)`, qui contient les deux notes.\n"
            },
            {
              "text": "Le tuple `(18, 7)`",
              "correct": false,
              "feedback": "Erreur : l'ordre des valeurs est celui écrit dans\n`return (mini, maxi)` : d'abord le minimum, puis le maximum.\n"
            }
          ],
          "explanation": "Une fonction renvoie une seule valeur, mais cette valeur peut être\nun p-uplet : c'est ainsi qu'on renvoie plusieurs résultats à la\nfois. On les récupère ensuite par `resultat[0]` et `resultat[1]`,\nou directement avec `bas, haut = bornes([7, 14, 9, 18, 11])`."
        },
        {
          "statement": "```python\ndef bornes(notes):\n    mini = notes[0]\n    maxi = notes[0]\n    for n in notes:\n        if n < mini:\n            mini = n\n        if n > maxi:\n            maxi = n\n    return (mini, maxi)\nresultat = bornes([16, 3, 12])\n```\nQue contient la variable `resultat` après l'exécution de ce code ?",
          "options": [
            {
              "text": "Le tuple `(3, 16)`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle retient la plus petite note, $3$, et la\nplus grande, $16$. L'instruction `return (mini, maxi)` renvoie un\np-uplet de deux valeurs, qui est affecté à `resultat`.\n"
            },
            {
              "text": "La liste `[3, 16]`",
              "correct": false,
              "feedback": "Erreur : les parenthèses de `return (mini, maxi)` construisent un\ntuple, pas une liste. Les valeurs sont justes, mais le type ne\nl'est pas.\n"
            },
            {
              "text": "L'entier `3`",
              "correct": false,
              "feedback": "Erreur : `return` ne s'arrête pas à la première valeur. Il renvoie\nle p-uplet entier `(mini, maxi)`, qui contient les deux notes.\n"
            },
            {
              "text": "Le tuple `(16, 3)`",
              "correct": false,
              "feedback": "Erreur : l'ordre des valeurs est celui écrit dans\n`return (mini, maxi)` : d'abord le minimum, puis le maximum.\n"
            }
          ],
          "explanation": "Une fonction renvoie une seule valeur, mais cette valeur peut être\nun p-uplet : c'est ainsi qu'on renvoie plusieurs résultats à la\nfois. On les récupère ensuite par `resultat[0]` et `resultat[1]`,\nou directement avec `bas, haut = bornes([16, 3, 12])`."
        }
      ]
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "tri"
      ],
      "title": "Méthodes sort et sorted",
      "partie": "listes",
      "statement": "Quelle est la différence entre\n`liste.sort()` et `sorted(liste)` ?",
      "options": [
        {
          "text": "La méthode `sort` est nettement plus lente à l'exécution",
          "correct": false,
          "feedback": "Les deux ont une\nperformance équivalente.\nLa différence porte sur\nla modification, ou non,\nde la liste d'origine.\n"
        },
        {
          "text": "Aucune différence, `sorted(liste)` appelle `liste.sort()`",
          "correct": false,
          "feedback": "Erreur : `sorted` construit une\n**nouvelle** liste triée sans toucher à\nl'original ; `sort` modifie la liste sur\nplace et renvoie `None`.\n"
        },
        {
          "text": "La fonction `sorted` ne fonctionne que sur des nombres",
          "correct": false,
          "feedback": "La fonction `sorted`\nfonctionne sur tout type\ncomparable (entiers,\nchaînes, tuples, etc.).\n"
        },
        {
          "text": "`sort` modifie la liste, `sorted` renvoie une copie triée",
          "correct": true,
          "feedback": "Bonne réponse : `sort` trie sur place\net renvoie `None`, `sorted` renvoie une\nnouvelle liste. Le choix dépend du besoin :\nsi l'on veut conserver\nl'original, on utilise\n`sorted` ; sinon, `sort`\nest préférable. La\nfonction `sorted`\nfonctionne aussi sur les\ntuples, contrairement à\n`sort`.\n"
        }
      ],
      "explanation": "Moyen mnémotechnique : `sorted`\n(« trié ») désigne un résultat,\nune nouvelle liste triée\nrenvoyée sans modifier\nl'original ; `sort` (« trier »)\ndésigne une action, qui modifie\nla liste sur place et renvoie\n`None`."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "comprehension-liste"
      ],
      "title": "Compréhension de liste",
      "partie": "listes",
      "statement": "Quelle expression construit la liste\n`[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]`\ndes carrés des entiers de $1$ à $10$ ?",
      "options": [
        {
          "text": "`[i ** 2 for i in range(1, 11)]`",
          "correct": true,
          "feedback": "Bonne réponse : `range(1, 11)` parcourt les\nentiers de $1$ à $10$ inclus, et `i ** 2`\ncalcule le carré de chacun. La compréhension\nrassemble ces dix carrés dans une liste.\n"
        },
        {
          "text": "`[i * 2 for i in range(1, 11)]`",
          "correct": false,
          "feedback": "Erreur : `i * 2` est le double de `i`, pas\nson carré. Cette expression construit\n`[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]`.\nLe carré s'écrit `i ** 2` ou `i * i`.\n"
        },
        {
          "text": "`[i ** 2 for i in range(10)]`",
          "correct": false,
          "feedback": "Erreur : `range(10)` commence à $0$ et\ns'arrête à $9$. Cette expression construit\n`[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]` :\nle carré de $0$ est en trop, celui de $10$\nmanque.\n"
        },
        {
          "text": "`[i ** 2 for i in range(1, 10)]`",
          "correct": false,
          "feedback": "Erreur : la borne de fin de `range` est\nexclue, donc `range(1, 10)` s'arrête à\n$9$. Cette expression construit\n`[1, 4, 9, 16, 25, 36, 49, 64, 81]`, sans\nle carré de $10$.\n"
        }
      ],
      "explanation": "Une compréhension de liste\n`[expression for i in range(a, b)]` construit\nla liste des valeurs de l'expression pour `i`\nallant de `a` à `b - 1`. Elle équivaut à une\nboucle qui part d'une liste vide et ajoute\nchaque valeur avec `append`. Les deux erreurs\nclassiques sont de confondre `*` et `**`, et\nde se tromper sur les bornes de `range`."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "reference-vs-copie"
      ],
      "title": "Référence et copie",
      "partie": "listes",
      "statement": "```python\na = [1, 2, 3]\nb = a\nb.append(4)\nprint(a)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "La liste `[4]`",
          "correct": false,
          "feedback": "La méthode `append`\najoute à la liste\nexistante, sans la\nremplacer.\n"
        },
        {
          "text": "La liste `[1, 2, 3]`",
          "correct": false,
          "feedback": "L'affectation `b = a` ne\ncrée pas une copie, mais\nattribue une nouvelle\nréférence vers la même\nliste.\n"
        },
        {
          "text": "Une exception est levée",
          "correct": false,
          "feedback": "Le code est parfaitement\nvalide.\n"
        },
        {
          "text": "La liste `[1, 2, 3, 4]`",
          "correct": true,
          "feedback": "Les variables `a` et `b`\ndésignent le **même** objet\nen mémoire. Modifier l'un\nrevient à modifier\nl'autre. Pour faire une\ncopie, on construit une\nnouvelle liste, par exemple\n`b = [x for x in a]`.\n"
        }
      ],
      "explanation": "C'est la notion essentielle\nd'**alias** : deux variables\npeuvent désigner le même\nobjet. Ce comportement diffère\ndes types immuables comme les\nentiers, où `b = a` n'a pas\ncet effet.",
      "variants": [
        {
          "statement": "```python\na = [1, 2, 3]\nb = a\nb.append(4)\nprint(a)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "La liste `[4]`",
              "correct": false,
              "feedback": "La méthode `append`\najoute à la liste\nexistante, sans la\nremplacer.\n"
            },
            {
              "text": "La liste `[1, 2, 3]`",
              "correct": false,
              "feedback": "L'affectation `b = a` ne\ncrée pas une copie, mais\nattribue une nouvelle\nréférence vers la même\nliste.\n"
            },
            {
              "text": "Une exception est levée",
              "correct": false,
              "feedback": "Le code est parfaitement\nvalide.\n"
            },
            {
              "text": "La liste `[1, 2, 3, 4]`",
              "correct": true,
              "feedback": "Les variables `a` et `b`\ndésignent le **même** objet\nen mémoire. Modifier l'un\nrevient à modifier\nl'autre. Pour faire une\ncopie, on construit une\nnouvelle liste, par exemple\n`b = [x for x in a]`.\n"
            }
          ],
          "explanation": "C'est la notion essentielle\nd'**alias** : deux variables\npeuvent désigner le même\nobjet. Ce comportement diffère\ndes types immuables comme les\nentiers, où `b = a` n'a pas\ncet effet."
        },
        {
          "statement": "```python\na = [7, 8]\nb = a\nb.append(9)\nprint(a)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "La liste `[9]`",
              "correct": false,
              "feedback": "La méthode `append`\najoute à la liste\nexistante, sans la\nremplacer.\n"
            },
            {
              "text": "La liste `[7, 8]`",
              "correct": false,
              "feedback": "L'affectation `b = a` ne\ncrée pas une copie, mais\nattribue une nouvelle\nréférence vers la même\nliste.\n"
            },
            {
              "text": "Une exception est levée",
              "correct": false,
              "feedback": "Le code est parfaitement\nvalide.\n"
            },
            {
              "text": "La liste `[7, 8, 9]`",
              "correct": true,
              "feedback": "Les variables `a` et `b`\ndésignent le **même** objet\nen mémoire. Modifier l'un\nrevient à modifier\nl'autre. Pour faire une\ncopie, on construit une\nnouvelle liste, par exemple\n`b = [x for x in a]`.\n"
            }
          ],
          "explanation": "C'est la notion essentielle\nd'**alias** : deux variables\npeuvent désigner le même\nobjet. Ce comportement diffère\ndes types immuables comme les\nentiers, où `b = a` n'a pas\ncet effet."
        },
        {
          "statement": "```python\na = [0, 5, 10, 15]\nb = a\nb.append(20)\nprint(a)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "La liste `[20]`",
              "correct": false,
              "feedback": "La méthode `append`\najoute à la liste\nexistante, sans la\nremplacer.\n"
            },
            {
              "text": "La liste `[0, 5, 10, 15]`",
              "correct": false,
              "feedback": "L'affectation `b = a` ne\ncrée pas une copie, mais\nattribue une nouvelle\nréférence vers la même\nliste.\n"
            },
            {
              "text": "Une exception est levée",
              "correct": false,
              "feedback": "Le code est parfaitement\nvalide.\n"
            },
            {
              "text": "La liste `[0, 5, 10, 15, 20]`",
              "correct": true,
              "feedback": "Les variables `a` et `b`\ndésignent le **même** objet\nen mémoire. Modifier l'un\nrevient à modifier\nl'autre. Pour faire une\ncopie, on construit une\nnouvelle liste, par exemple\n`b = [x for x in a]`.\n"
            }
          ],
          "explanation": "C'est la notion essentielle\nd'**alias** : deux variables\npeuvent désigner le même\nobjet. Ce comportement diffère\ndes types immuables comme les\nentiers, où `b = a` n'a pas\ncet effet."
        }
      ]
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "parcours",
        "variable-de-boucle",
        "mutabilite"
      ],
      "title": "Réaffecter la variable de boucle",
      "partie": "listes",
      "statement": "```python\nl = [1, 2, 3]\nfor x in l:\n    x = x * 2\nprint(l)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `[2, 4, 6]`",
          "correct": false,
          "feedback": "Erreur : `x = x * 2` crée un nouvel entier et le\nrange dans `x`, sans rien écrire dans la liste.\nPour doubler les éléments, il faut écrire\n`l[i] = l[i] * 2` en parcourant les indices.\n"
        },
        {
          "text": "Il affiche `[1, 2, 3]`",
          "correct": true,
          "feedback": "Bonne réponse : réaffecter la variable de boucle\nne modifie jamais la liste. À chaque tour, `x`\ndésigne un élément, puis reçoit un nouvel\nentier ; la liste n'est pas touchée.\n"
        },
        {
          "text": "Il affiche `[1, 2, 3, 2, 4, 6]`",
          "correct": false,
          "feedback": "Erreur : aucune instruction n'ajoute d'élément\nà `l`. Seule la méthode `append` allongerait la\nliste ; ici on ne fait que réaffecter `x`.\n"
        },
        {
          "text": "Il déclenche une erreur, car la liste est modifiée pendant le parcours",
          "correct": false,
          "feedback": "Erreur : la liste n'est justement pas modifiée,\net réaffecter la variable de boucle est de toute\nfaçon autorisé. Le code s'exécute sans erreur.\n"
        }
      ],
      "explanation": "Pour transformer les éléments d'une liste, on\nparcourt les indices et on écrit dans la liste :\n`for i in range(len(l)): l[i] = l[i] * 2`. La\nvariable `x` d'un `for x in l` n'est qu'un nom\ntemporaire posé sur chaque élément."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "cle-dictionnaire"
      ],
      "title": "Tuple comme clé de dictionnaire",
      "partie": "choisir",
      "statement": "Pourquoi un tuple peut-il être\nutilisé comme **clé** de\ndictionnaire, alors qu'une liste\nne le peut pas ?",
      "options": [
        {
          "text": "Aucune des deux structures ne peut servir de clé",
          "correct": false,
          "feedback": "Un tuple peut bien servir\nde clé, à condition que\ntous ses éléments soient\neux-mêmes immuables.\n"
        },
        {
          "text": "Aucune raison particulière",
          "correct": false,
          "feedback": "La distinction a une raison technique : une clé doit rester\nidentique pour que le dictionnaire retrouve la valeur associée.\n"
        },
        {
          "text": "Parce qu'un tuple est immuable, contrairement à une liste",
          "correct": true,
          "feedback": "Les clés de dictionnaire doivent rester identiques dans le temps.\nUne liste, modifiable, ne peut pas servir de clé : on pourrait la\nchanger après l'avoir utilisée, et le dictionnaire ne retrouverait\nplus la valeur associée.\n"
        },
        {
          "text": "Uniquement pour des raisons purement historiques et anciennes",
          "correct": false,
          "feedback": "La raison est strictement\ntechnique, et non\nhistorique.\n"
        }
      ],
      "explanation": "Les types immuables classiques peuvent servir de clés : entiers,\nflottants, chaînes et tuples, à condition que ces derniers ne\ncontiennent eux-mêmes que des valeurs immuables. Les listes et les\ndictionnaires, modifiables, ne le peuvent pas."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "longueur-tuple"
      ],
      "title": "Tuple à un élément",
      "partie": "tuples",
      "statement": "Que vaut l'expression `len((42))` ?",
      "options": [
        {
          "text": "La valeur $0$, car le tuple `(42)` est considéré comme vide",
          "correct": false,
          "feedback": "Erreur : `(42)` n'est pas un tuple du\ntout, et `len` lève une exception sur un\nentier.\n"
        },
        {
          "text": "Une exception `TypeError`",
          "correct": true,
          "feedback": "Bonne réponse : `(42)` n'est pas un tuple\nmais l'entier 42 entre parenthèses, et\n`len` n'agit pas sur un entier. Pour créer\nun tuple à un seul élément, il faut une\nvirgule : `(42,)`.\n"
        },
        {
          "text": "La valeur $42$, l'unique élément du tuple",
          "correct": false,
          "feedback": "Erreur : `len` ne renvoie pas la valeur\nde son argument, et `(42)` n'est pas un\ntuple.\n"
        },
        {
          "text": "La valeur entière $1$, comme on pourrait l'attendre",
          "correct": false,
          "feedback": "L'expression `(42)` n'est\npas un tuple, mais\nsimplement l'entier $42$\nentouré de parenthèses de\ngroupement.\n"
        }
      ],
      "explanation": "C'est un piège classique : il\nfaut toujours penser à la\nvirgule pour les tuples à un\nseul élément. Pour vérifier,\n`type((42))` renvoie `int`,\ntandis que `type((42,))`\nrenvoie `tuple`."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "alias",
        "concatenation",
        "reaffectation"
      ],
      "title": "Alias et concaténation",
      "partie": "listes",
      "statement": "```python\nl = [1, 2, 3]\nm = l\nl = l + [4]\nprint(m)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `[1, 2, 3, 4]`",
          "correct": false,
          "feedback": "Erreur : l'alias `m` suivrait `l` si la liste\nétait modifiée sur place, par `l.append(4)` ou\n`l += [4]`. Mais `l + [4]` construit une\n**nouvelle** liste, que `l` reçoit ; `m` désigne\ntoujours l'ancienne.\n"
        },
        {
          "text": "Il affiche `[1, 2, 3, [4]]`",
          "correct": false,
          "feedback": "Erreur : l'opérateur `+` concatène les éléments\nsans imbriquer la liste `[4]`, et surtout il\nproduit une nouvelle liste : `m` n'en voit rien.\n"
        },
        {
          "text": "Il affiche `[1, 2, 3]`",
          "correct": true,
          "feedback": "Bonne réponse : après `m = l`, les deux noms\ndésignent la même liste. Puis `l + [4]` crée\nune nouvelle liste `[1, 2, 3, 4]`, affectée à\n`l` seul. `m` garde l'ancienne liste, intacte.\n"
        },
        {
          "text": "Il déclenche une erreur, car on ne peut pas additionner deux listes",
          "correct": false,
          "feedback": "Erreur : `+` entre deux listes est défini et\nproduit leur concaténation. Seule l'addition\nd'une liste et d'un entier, comme `l + 4`,\nlève `TypeError`.\n"
        }
      ],
      "explanation": "Deux façons d'allonger une liste n'ont pas le même\neffet sur les alias : `l.append(4)` et `l += [4]`\nmodifient la liste partagée, visible depuis `m` ;\n`l = l + [4]` réaffecte `l` à une nouvelle liste\net rompt le lien avec `m`.",
      "variants": [
        {
          "statement": "```python\nl = [1, 2, 3]\nm = l\nl = l + [4]\nprint(m)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `[1, 2, 3, 4]`",
              "correct": false,
              "feedback": "Erreur : l'alias `m` suivrait `l` si la liste\nétait modifiée sur place, par `l.append(4)` ou\n`l += [4]`. Mais `l + [4]` construit une\n**nouvelle** liste, que `l` reçoit ; `m` désigne\ntoujours l'ancienne.\n"
            },
            {
              "text": "Il affiche `[1, 2, 3, [4]]`",
              "correct": false,
              "feedback": "Erreur : l'opérateur `+` concatène les éléments\nsans imbriquer la liste `[4]`, et surtout il\nproduit une nouvelle liste : `m` n'en voit rien.\n"
            },
            {
              "text": "Il affiche `[1, 2, 3]`",
              "correct": true,
              "feedback": "Bonne réponse : après `m = l`, les deux noms\ndésignent la même liste. Puis `l + [4]` crée\nune nouvelle liste `[1, 2, 3, 4]`, affectée à\n`l` seul. `m` garde l'ancienne liste, intacte.\n"
            },
            {
              "text": "Il déclenche une erreur, car on ne peut pas additionner deux listes",
              "correct": false,
              "feedback": "Erreur : `+` entre deux listes est défini et\nproduit leur concaténation. Seule l'addition\nd'une liste et d'un entier, comme `l + 4`,\nlève `TypeError`.\n"
            }
          ],
          "explanation": "Deux façons d'allonger une liste n'ont pas le même\neffet sur les alias : `l.append(4)` et `l += [4]`\nmodifient la liste partagée, visible depuis `m` ;\n`l = l + [4]` réaffecte `l` à une nouvelle liste\net rompt le lien avec `m`."
        },
        {
          "statement": "```python\nl = [5, 5]\nm = l\nl = l + [6]\nprint(m)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `[5, 5, 6]`",
              "correct": false,
              "feedback": "Erreur : l'alias `m` suivrait `l` si la liste\nétait modifiée sur place, par `l.append(6)` ou\n`l += [6]`. Mais `l + [6]` construit une\n**nouvelle** liste, que `l` reçoit ; `m` désigne\ntoujours l'ancienne.\n"
            },
            {
              "text": "Il affiche `[5, 5, [6]]`",
              "correct": false,
              "feedback": "Erreur : l'opérateur `+` concatène les éléments\nsans imbriquer la liste `[6]`, et surtout il\nproduit une nouvelle liste : `m` n'en voit rien.\n"
            },
            {
              "text": "Il affiche `[5, 5]`",
              "correct": true,
              "feedback": "Bonne réponse : après `m = l`, les deux noms\ndésignent la même liste. Puis `l + [6]` crée\nune nouvelle liste `[5, 5, 6]`, affectée à\n`l` seul. `m` garde l'ancienne liste, intacte.\n"
            },
            {
              "text": "Il déclenche une erreur, car on ne peut pas additionner deux listes",
              "correct": false,
              "feedback": "Erreur : `+` entre deux listes est défini et\nproduit leur concaténation. Seule l'addition\nd'une liste et d'un entier, comme `l + 6`,\nlève `TypeError`.\n"
            }
          ],
          "explanation": "Deux façons d'allonger une liste n'ont pas le même\neffet sur les alias : `l.append(6)` et `l += [6]`\nmodifient la liste partagée, visible depuis `m` ;\n`l = l + [6]` réaffecte `l` à une nouvelle liste\net rompt le lien avec `m`."
        },
        {
          "statement": "```python\nl = [2, 4, 8]\nm = l\nl = l + [16]\nprint(m)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `[2, 4, 8, 16]`",
              "correct": false,
              "feedback": "Erreur : l'alias `m` suivrait `l` si la liste\nétait modifiée sur place, par `l.append(16)` ou\n`l += [16]`. Mais `l + [16]` construit une\n**nouvelle** liste, que `l` reçoit ; `m` désigne\ntoujours l'ancienne.\n"
            },
            {
              "text": "Il affiche `[2, 4, 8, [16]]`",
              "correct": false,
              "feedback": "Erreur : l'opérateur `+` concatène les éléments\nsans imbriquer la liste `[16]`, et surtout il\nproduit une nouvelle liste : `m` n'en voit rien.\n"
            },
            {
              "text": "Il affiche `[2, 4, 8]`",
              "correct": true,
              "feedback": "Bonne réponse : après `m = l`, les deux noms\ndésignent la même liste. Puis `l + [16]` crée\nune nouvelle liste `[2, 4, 8, 16]`, affectée à\n`l` seul. `m` garde l'ancienne liste, intacte.\n"
            },
            {
              "text": "Il déclenche une erreur, car on ne peut pas additionner deux listes",
              "correct": false,
              "feedback": "Erreur : `+` entre deux listes est défini et\nproduit leur concaténation. Seule l'addition\nd'une liste et d'un entier, comme `l + 16`,\nlève `TypeError`.\n"
            }
          ],
          "explanation": "Deux façons d'allonger une liste n'ont pas le même\neffet sur les alias : `l.append(16)` et `l += [16]`\nmodifient la liste partagée, visible depuis `m` ;\n`l = l + [16]` réaffecte `l` à une nouvelle liste\net rompt le lien avec `m`."
        }
      ]
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "tuple",
        "mutabilite",
        "liste-de-tuples"
      ],
      "title": "Corriger une note dans une liste de p-uplets",
      "partie": "tuples",
      "statement": "Les notes d'une classe sont rangées dans\n`eleves = [(\"Ana\", 12), (\"Bilal\", 9), (\"Chloé\", 15)]`,\nun p-uplet par élève. La note de Bilal est\nfausse, il a eu $11$. Quelle instruction\ncorrige la liste sans erreur ?",
      "options": [
        {
          "text": "`eleves[1][1] = 11`",
          "correct": false,
          "feedback": "Erreur : `eleves[1]` est le p-uplet\n`(\"Bilal\", 9)`, et un p-uplet est immuable.\nPython lève\n`TypeError: 'tuple' object does not support item assignment`.\n"
        },
        {
          "text": "`eleves[1] = (\"Bilal\", 11)`",
          "correct": true,
          "feedback": "Bonne réponse : on ne modifie pas le p-uplet,\non le remplace. La liste, elle, est mutable :\nl'affectation range un nouveau p-uplet à\nl'indice $1$, et la liste vaut\n`[(\"Ana\", 12), (\"Bilal\", 11), (\"Chloé\", 15)]`.\n"
        },
        {
          "text": "`eleves[\"Bilal\"] = 11`",
          "correct": false,
          "feedback": "Erreur : une liste s'indexe par un entier,\npas par une chaîne ; cette écriture est celle\nd'un dictionnaire. Python lève\n`TypeError: list indices must be integers or slices, not str`.\n"
        },
        {
          "text": "`eleves[1].append(11)`",
          "correct": false,
          "feedback": "Erreur : `eleves[1]` est un p-uplet, qui n'a\npas de méthode `append`. Python lève\n`AttributeError: 'tuple' object has no attribute 'append'`.\nEt même sur une liste, on aurait ajouté $11$\nau lieu de remplacer $9$.\n"
        }
      ],
      "explanation": "Dans une liste de p-uplets, chaque enregistrement\nest immuable, mais la liste qui les contient est\nmutable. Pour corriger un champ, on construit un\nnouveau p-uplet et on l'affecte à l'indice voulu :\n`eleves[1] = (\"Bilal\", 11)`. Toute tentative de\nmodifier le p-uplet sur place lève une exception."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "liste-vs-tuple-perf"
      ],
      "title": "Tuples pour les constantes",
      "partie": "choisir",
      "statement": "Pour stocker des **constantes**\nque l'on ne modifiera jamais,\nquelle structure est la plus\nindiquée ?",
      "options": [
        {
          "text": "Un dictionnaire associant des clés à des valeurs",
          "correct": false,
          "feedback": "Le dictionnaire associe\ndes clés à des valeurs.\nPour de simples\nconstantes, c'est plus\nlourd qu'un tuple.\n"
        },
        {
          "text": "Un tuple, immuable",
          "correct": true,
          "feedback": "Bonne réponse : le tuple signale\nl'immuabilité et reste un peu plus\nléger qu'une liste. Par exemple,\n`JOURS = (\"lun\", \"mar\",\n\"mer\", ...)`. Le lecteur\ndu code comprend\nimmédiatement que ces\ndonnées ne changeront\npas.\n"
        },
        {
          "text": "Une liste, que l'on pourra compléter si les constantes évoluent",
          "correct": false,
          "feedback": "Erreur : une liste fonctionnerait, mais\nson caractère modifiable contredit\nl'intention (des données figées) et\nn'avertit pas le lecteur du code.\n"
        },
        {
          "text": "Une chaîne de caractères, les valeurs séparées par des virgules",
          "correct": false,
          "feedback": "Une chaîne n'est pas\nadaptée pour stocker des\néléments séparés, sauf\ndans des cas\nparticuliers.\n"
        }
      ],
      "explanation": "Convention Python : on\nutilise les tuples pour les\ndonnées figées, et les\nlistes pour les données qui\névoluent au cours du\nprogramme. Cette distinction\naméliore la lisibilité du\ncode."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "partie": "choisir",
      "statement": "Parmi les affirmations suivantes\nsur les tuples et les listes,\nlaquelle est **fausse** ?",
      "options": [
        {
          "text": "Les deux structures supportent l'indexation et la fonction `len`",
          "correct": false,
          "feedback": "Cette affirmation est correcte : on peut écrire `s[0]` ou `len(s)`\naussi bien sur une liste que sur un tuple.\n"
        },
        {
          "text": "Les listes peuvent être utilisées comme clés de dictionnaire",
          "correct": true,
          "feedback": "Cette affirmation est fausse (donc c'est la bonne réponse). Les\nlistes sont modifiables, donc interdites comme clés de\ndictionnaire. Les tuples peuvent l'être, à condition que tous leurs\néléments soient eux-mêmes immuables.\n"
        },
        {
          "text": "`liste = []` crée une liste vide ; `t = ()` crée un tuple vide",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte.\n"
        },
        {
          "text": "Une liste est mutable, un tuple est immuable",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte. C'est même la\ndifférence la plus\nfondamentale entre les\ndeux structures.\n"
        }
      ],
      "explanation": "Mémo utile : les clés d'un\ndictionnaire doivent être\nstables dans le temps. Tout\nce qui est mutable n'offre\npas cette stabilité, et ne\npeut donc pas servir de\nclé."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "parcours",
        "inversion",
        "construction-liste"
      ],
      "title": "Renverser une liste",
      "partie": "listes",
      "statement": "On dispose d'une liste `t = [10, 20, 30, 40, 50]` et\nl'on veut construire une nouvelle liste `r` contenant\nles mêmes éléments dans l'ordre inverse, soit\n`[50, 40, 30, 20, 10]`. Quel code convient ?",
      "options": [
        {
          "text": "```python\nr = []\nfor x in t:\n    r.append(x)\n```\n",
          "correct": false,
          "feedback": "Erreur : chaque élément est ajouté à la fin de\n`r` dans l'ordre de parcours. On obtient une\nsimple copie `[10, 20, 30, 40, 50]`.\n"
        },
        {
          "text": "```python\nr = []\nfor x in t:\n    r = [x] + r\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : à chaque tour, `[x] + r` place\nle nouvel élément **devant** ceux déjà rangés.\n`r` vaut successivement `[10]`, `[20, 10]`,\n`[30, 20, 10]`, jusqu'à `[50, 40, 30, 20, 10]`.\n"
        },
        {
          "text": "```python\nr = []\nfor i in range(len(t) - 1):\n    r.append(t[len(t) - 1 - i])\n```",
          "correct": false,
          "feedback": "Erreur : l'indice `len(t) - 1 - i` lit bien la\nliste depuis la fin, mais `range(len(t) - 1)` ne\nfait que $4$ tours. Le résultat\n`[50, 40, 30, 20]` oublie le premier élément, $10$.\n"
        },
        {
          "text": "```python\nr = []\nfor i in range(len(t)):\n    r.append(t[len(t) - i])\n```\n",
          "correct": false,
          "feedback": "Erreur : au premier tour, `t[len(t) - 0]` vaut\n`t[5]`, hors de la liste, d'où une `IndexError`.\nIl faudrait écrire `t[len(t) - 1 - i]`.\n"
        }
      ],
      "explanation": "Construire la liste renversée revient à insérer\nchaque élément en tête, ou à lire la liste de la fin\nvers le début avec les indices `len(t) - 1 - i`.\nDans les deux cas, `t` n'est pas modifiée."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "parcours",
        "compteur",
        "indices"
      ],
      "title": "Compter les 5 et repérer le premier",
      "partie": "listes",
      "statement": "```python\nt = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]\ncompteur = 0\npremier = -1\nfor i in range(len(t)):\n    if t[i] == 5:\n        compteur = compteur + 1\n        if premier == -1:\n            premier = i\nprint(compteur, premier)\n```\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`3 10`",
          "correct": false,
          "feedback": "Erreur : ce serait l'indice de la **dernière** occurrence. Le test\n`premier == -1` n'est vrai qu'au premier $5$ rencontré ; ensuite\n`premier` vaut $4$ et n'est plus modifié.\n"
        },
        {
          "text": "`3 4`",
          "correct": true,
          "feedback": "Bonne réponse : la valeur $5$ apparaît trois fois, aux indices $4$, $8$ et $10$. La variable `premier` ne reçoit un indice que tant\nqu'elle vaut $-1$, donc seulement à la première occurrence : $4$.\n"
        },
        {
          "text": "`3 5`",
          "correct": false,
          "feedback": "Erreur : les indices commencent à $0$. Le premier $5$ est le\ncinquième élément de la liste, mais son indice est $4$.\n"
        },
        {
          "text": "`4 3`",
          "correct": false,
          "feedback": "Erreur : les rôles sont inversés. `compteur` vaut le nombre\nd'occurrences, $3$, et `premier` l'indice de la première, $4$ ;\n`print` les affiche dans cet ordre.\n"
        }
      ],
      "explanation": "Un seul parcours suffit pour compter les occurrences et retenir la\nposition de la première. La valeur de départ $-1$, qui n'est jamais\nun indice produit par `range`, signale que la valeur n'a pas encore\nété rencontrée.",
      "variants": [
        {
          "statement": "```python\nt = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]\ncompteur = 0\npremier = -1\nfor i in range(len(t)):\n    if t[i] == 5:\n        compteur = compteur + 1\n        if premier == -1:\n            premier = i\nprint(compteur, premier)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`3 10`",
              "correct": false,
              "feedback": "Erreur : ce serait l'indice de la **dernière** occurrence. Le test\n`premier == -1` n'est vrai qu'au premier $5$ rencontré ; ensuite\n`premier` vaut $4$ et n'est plus modifié.\n"
            },
            {
              "text": "`3 4`",
              "correct": true,
              "feedback": "Bonne réponse : la valeur $5$ apparaît trois fois, aux indices $4$, $8$ et $10$. La variable `premier` ne reçoit un indice que tant\nqu'elle vaut $-1$, donc seulement à la première occurrence : $4$.\n"
            },
            {
              "text": "`3 5`",
              "correct": false,
              "feedback": "Erreur : les indices commencent à $0$. Le premier $5$ est le\ncinquième élément de la liste, mais son indice est $4$.\n"
            },
            {
              "text": "`4 3`",
              "correct": false,
              "feedback": "Erreur : les rôles sont inversés. `compteur` vaut le nombre\nd'occurrences, $3$, et `premier` l'indice de la première, $4$ ;\n`print` les affiche dans cet ordre.\n"
            }
          ],
          "explanation": "Un seul parcours suffit pour compter les occurrences et retenir la\nposition de la première. La valeur de départ $-1$, qui n'est jamais\nun indice produit par `range`, signale que la valeur n'a pas encore\nété rencontrée."
        },
        {
          "statement": "```python\nt = [2, 7, 1, 8, 2, 8, 1, 8, 4]\ncompteur = 0\npremier = -1\nfor i in range(len(t)):\n    if t[i] == 2:\n        compteur = compteur + 1\n        if premier == -1:\n            premier = i\nprint(compteur, premier)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`2 4`",
              "correct": false,
              "feedback": "Erreur : ce serait l'indice de la **dernière** occurrence. Le test\n`premier == -1` n'est vrai qu'au premier $2$ rencontré ; ensuite\n`premier` vaut $0$ et n'est plus modifié.\n"
            },
            {
              "text": "`2 0`",
              "correct": true,
              "feedback": "Bonne réponse : la valeur $2$ apparaît deux fois, aux indices $0$ et $4$. La variable `premier` ne reçoit un indice que tant\nqu'elle vaut $-1$, donc seulement à la première occurrence : $0$.\n"
            },
            {
              "text": "`2 1`",
              "correct": false,
              "feedback": "Erreur : les indices commencent à $0$. Le premier $2$ est le\npremier élément de la liste, mais son indice est $0$.\n"
            },
            {
              "text": "`0 2`",
              "correct": false,
              "feedback": "Erreur : les rôles sont inversés. `compteur` vaut le nombre\nd'occurrences, $2$, et `premier` l'indice de la première, $0$ ;\n`print` les affiche dans cet ordre.\n"
            }
          ],
          "explanation": "Un seul parcours suffit pour compter les occurrences et retenir la\nposition de la première. La valeur de départ $-1$, qui n'est jamais\nun indice produit par `range`, signale que la valeur n'a pas encore\nété rencontrée."
        },
        {
          "statement": "```python\nt = [6, 0, 6, 6, 1, 9]\ncompteur = 0\npremier = -1\nfor i in range(len(t)):\n    if t[i] == 6:\n        compteur = compteur + 1\n        if premier == -1:\n            premier = i\nprint(compteur, premier)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`3 3`",
              "correct": false,
              "feedback": "Erreur : ce serait l'indice de la **dernière** occurrence. Le test\n`premier == -1` n'est vrai qu'au premier $6$ rencontré ; ensuite\n`premier` vaut $0$ et n'est plus modifié.\n"
            },
            {
              "text": "`3 0`",
              "correct": true,
              "feedback": "Bonne réponse : la valeur $6$ apparaît trois fois, aux indices $0$, $2$ et $3$. La variable `premier` ne reçoit un indice que tant\nqu'elle vaut $-1$, donc seulement à la première occurrence : $0$.\n"
            },
            {
              "text": "`3 1`",
              "correct": false,
              "feedback": "Erreur : les indices commencent à $0$. Le premier $6$ est le\npremier élément de la liste, mais son indice est $0$.\n"
            },
            {
              "text": "`0 3`",
              "correct": false,
              "feedback": "Erreur : les rôles sont inversés. `compteur` vaut le nombre\nd'occurrences, $3$, et `premier` l'indice de la première, $0$ ;\n`print` les affiche dans cet ordre.\n"
            }
          ],
          "explanation": "Un seul parcours suffit pour compter les occurrences et retenir la\nposition de la première. La valeur de départ $-1$, qui n'est jamais\nun indice produit par `range`, signale que la valeur n'a pas encore\nété rencontrée."
        }
      ]
    },
    {
      "id": "q28",
      "difficulty": 2,
      "skills": [
        "append",
        "extend",
        "concatenation"
      ],
      "title": "append, extend et concaténation",
      "partie": "listes",
      "statement": "On exécute :\n\n```python\na = [1, 2, 3]\nb = [4, 5]\na.append(b)\n```\n\nQue vaut la liste `a` après cette exécution ?",
      "options": [
        {
          "text": "Le code lève une erreur",
          "correct": false,
          "feedback": "Erreur : la méthode `append` accepte n'importe\nquel objet, y compris une liste. Le code\ns'exécute sans erreur, même si le résultat\npeut surprendre.\n"
        },
        {
          "text": "`[1, 2, 3, [4, 5]]`",
          "correct": true,
          "feedback": "Bonne réponse : `append` ajoute son paramètre\ntel quel à la fin de la liste, sans déballage.\nComme `b` est lui-même une liste, on obtient\nune liste imbriquée.\n"
        },
        {
          "text": "`[1, 2, 3, 4, 5]`",
          "correct": false,
          "feedback": "Erreur : ce serait le résultat de\n`a = a + b`, qui enchaîne les éléments de `b`\nun par un après ceux de `a`. La méthode\n`append`, elle, ajoute son paramètre comme un\n**unique élément**.\n"
        },
        {
          "text": "`[[1, 2, 3], 4, 5]`",
          "correct": false,
          "feedback": "Erreur : `append` ajoute en **fin** de liste,\npas au début. Et c'est `b` qui est ajouté, pas\n`a`.\n"
        }
      ],
      "explanation": "Deux opérations à distinguer : `a.append(x)`\najoute `x` à la fin de `a` comme un seul élément,\nen modifiant `a` sur place ; `a + b` produit une\n**nouvelle** liste formée des éléments de `a`\npuis de ceux de `b`, sans modifier ni `a` ni `b`.\nPour obtenir `[1, 2, 3, 4, 5]` dans `a`, on écrit\ndonc `a = a + b`. Confondre les deux est l'une des\nerreurs les plus fréquentes en Python."
    },
    {
      "id": "q29",
      "difficulty": 1,
      "skills": [
        "tuple",
        "heterogene"
      ],
      "title": "Un tuple de types différents",
      "partie": "tuples",
      "statement": "Le tuple `eleve = (\"Ana\", 15, True)`\nest-il valide en Python ?",
      "options": [
        {
          "text": "Oui : un tuple regroupe des valeurs de types différents",
          "correct": true,
          "feedback": "Bonne réponse : un tuple, comme une\nliste, accepte des éléments de types\ndifférents. C'est même son emploi le\nplus courant : regrouper les\ncaractéristiques d'une même chose,\nici un prénom, une note et un\nbooléen.\n"
        },
        {
          "text": "Non : tous les éléments d'un tuple doivent avoir le même type",
          "correct": false,
          "feedback": "Erreur : cette contrainte n'existe ni\npour les tuples ni pour les listes.\nElle existe dans d'autres langages,\ncomme les tableaux du langage C.\n"
        },
        {
          "text": "Non : un tuple n'accepte que des nombres",
          "correct": false,
          "feedback": "Erreur : un tuple accepte n'importe\nquel type de valeur, y compris des\nchaînes, des booléens, et même\nd'autres tuples.\n"
        },
        {
          "text": "Oui, mais seulement si on l'écrit sans parenthèses",
          "correct": false,
          "feedback": "Erreur : les parenthèses ne changent\nrien aux types acceptés. Ce sont les\nvirgules qui créent le tuple.\n"
        }
      ],
      "explanation": "Un tuple sert justement à réunir des\ninformations de natures différentes sur\nun même objet : `(\"Ana\", 15, True)` se\nlit comme une fiche à trois champs."
    },
    {
      "id": "q30",
      "difficulty": 1,
      "skills": [
        "tuple",
        "conversion"
      ],
      "title": "D'une liste au tuple",
      "partie": "tuples",
      "statement": "Comment obtient-on un **tuple** à partir\nde la liste `l = [1, 2, 3]` ?",
      "options": [
        {
          "text": "Avec `t = tuple(l)`",
          "correct": true,
          "feedback": "Bonne réponse : `tuple(l)` construit un\nnouveau tuple avec les mêmes éléments.\nLa conversion inverse s'écrit\n`list(t)`.\n"
        },
        {
          "text": "Avec `t = l.tuple()`",
          "correct": false,
          "feedback": "Erreur : les listes n'ont pas de\nméthode `tuple`. La conversion se fait\npar la fonction `tuple`.\n"
        },
        {
          "text": "Avec `t = (l)`",
          "correct": false,
          "feedback": "Erreur : des parenthèses autour d'une\nliste ne changent rien, `t` reste la\nmême liste.\n"
        },
        {
          "text": "Avec `t = l.freeze()`",
          "correct": false,
          "feedback": "Erreur : cette méthode n'existe pas en\nPython.\n"
        }
      ],
      "explanation": "Les deux conversions se font par les\nfonctions qui portent le nom du type :\n`tuple(l)` dans un sens, `list(t)` dans\nl'autre. Chacune crée une nouvelle\nstructure, l'originale reste intacte."
    },
    {
      "id": "q31",
      "difficulty": 1,
      "skills": [
        "tuple",
        "echange-variables",
        "unpacking"
      ],
      "title": "Échanger deux variables",
      "partie": "tuples",
      "statement": "```python\na = 1\nb = 2\na, b = b, a\nprint(a, b)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `2 1`",
          "correct": true,
          "feedback": "Bonne réponse : la partie droite\nconstruit d'abord le tuple `(2, 1)`,\npuis ses deux valeurs sont rangées\ndans `a` et `b`. L'échange se fait\nsans variable intermédiaire.\n"
        },
        {
          "text": "Il affiche `1 2`",
          "correct": false,
          "feedback": "Erreur : les valeurs sont bien\néchangées. Ce serait le résultat sans\nla troisième ligne.\n"
        },
        {
          "text": "Il affiche `2 2`, car `a` est écrasé avant d'être lu",
          "correct": false,
          "feedback": "Erreur : c'est ce qui arriverait avec\ndeux affectations successives\n(`a = b` puis `b = a`). Ici la partie\ndroite est entièrement évaluée avant\ntoute affectation.\n"
        },
        {
          "text": "Il déclenche une erreur de syntaxe",
          "correct": false,
          "feedback": "Erreur : cette écriture est correcte\nen Python, c'est même la façon\nhabituelle d'échanger deux variables.\n"
        }
      ],
      "explanation": "Cette ligne cache un tuple : Python\névalue `b, a`, ce qui donne `(2, 1)`,\npuis déballe ce tuple dans `a, b`. D'où\nl'échange en une seule instruction."
    },
    {
      "id": "q32",
      "difficulty": 1,
      "skills": [
        "tuple",
        "methodes"
      ],
      "title": "Ce qu'un tuple sait faire",
      "partie": "tuples",
      "statement": "On dispose de `t = (3, 1, 4, 1, 5)`.\nQuelle instruction est **valide** ?",
      "options": [
        {
          "text": "`t.count(1)`, qui renvoie $2$",
          "correct": true,
          "feedback": "Bonne réponse : `count` compte les\noccurrences d'une valeur et `index`\ndonne la position de la première.\nCes deux méthodes ne modifient rien,\nelles sont donc permises sur un\ntuple.\n"
        },
        {
          "text": "`t.append(9)`, qui ajoute $9$ à la fin",
          "correct": false,
          "feedback": "Erreur : `append` modifierait le\ntuple, ce qui est impossible.\nL'exécution lève une exception\n`AttributeError`.\n"
        },
        {
          "text": "`t.sort()`, qui range les valeurs dans l'ordre",
          "correct": false,
          "feedback": "Erreur : `sort` trie sur place, donc\nmodifie. Sur un tuple, on passe par\n`sorted(t)`, qui renvoie une nouvelle\nliste triée.\n"
        },
        {
          "text": "`t[0] = 7`, qui remplace le premier élément",
          "correct": false,
          "feedback": "Erreur : remplacer un élément revient à\nmodifier le tuple, ce qui lève une\nexception `TypeError`.\n"
        }
      ],
      "explanation": "Un tuple ne garde que les méthodes qui\nne modifient rien : `count` et `index`.\nToutes celles qui transforment la\nstructure (`append`, `sort`, `remove`)\nappartiennent aux seules listes."
    },
    {
      "id": "q33",
      "difficulty": 2,
      "skills": [
        "tuple",
        "parcours",
        "indices"
      ],
      "title": "Parcourir un tuple par ses indices",
      "partie": "tuples",
      "statement": "```python\nt = (10, 20, 30)\nfor i in range(len(t)):\n    print(t[i])\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `10`, `20` puis `30`, une valeur par ligne",
          "correct": true,
          "feedback": "Bonne réponse : la lecture par indice\nfonctionne sur un tuple comme sur une\nliste. Seule l'écriture est interdite.\n"
        },
        {
          "text": "Il déclenche une exception, un tuple n'ayant pas d'indices",
          "correct": false,
          "feedback": "Erreur : les éléments d'un tuple sont\nnumérotés à partir de $0$, exactement\ncomme ceux d'une liste.\n"
        },
        {
          "text": "Il affiche `0`, `1` puis `2`, c'est-à-dire les indices",
          "correct": false,
          "feedback": "Erreur : `t[i]` désigne la valeur\nrangée à l'indice `i`, pas l'indice\nlui-même.\n"
        },
        {
          "text": "Il affiche `(10, 20, 30)` en une seule ligne",
          "correct": false,
          "feedback": "Erreur : le `print` est à l'intérieur\nde la boucle, il s'exécute une fois\npar tour.\n"
        }
      ],
      "explanation": "Un tuple se lit exactement comme une\nliste : par indice, par parcours direct\n`for v in t`, ou avec `len`. La seule\ndifférence apparaît quand on veut\nécrire.",
      "variants": [
        {
          "statement": "```python\nt = (10, 20, 30)\nfor i in range(len(t)):\n    print(t[i])\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `10`, `20` puis `30`, une valeur par ligne",
              "correct": true,
              "feedback": "Bonne réponse : la lecture par indice\nfonctionne sur un tuple comme sur une\nliste. Seule l'écriture est interdite.\n"
            },
            {
              "text": "Il déclenche une exception, un tuple n'ayant pas d'indices",
              "correct": false,
              "feedback": "Erreur : les éléments d'un tuple sont\nnumérotés à partir de $0$, exactement\ncomme ceux d'une liste.\n"
            },
            {
              "text": "Il affiche `0`, `1` puis `2`, c'est-à-dire les indices",
              "correct": false,
              "feedback": "Erreur : `t[i]` désigne la valeur\nrangée à l'indice `i`, pas l'indice\nlui-même.\n"
            },
            {
              "text": "Il affiche `(10, 20, 30)` en une seule ligne",
              "correct": false,
              "feedback": "Erreur : le `print` est à l'intérieur\nde la boucle, il s'exécute une fois\npar tour.\n"
            }
          ],
          "explanation": "Un tuple se lit exactement comme une\nliste : par indice, par parcours direct\n`for v in t`, ou avec `len`. La seule\ndifférence apparaît quand on veut\nécrire."
        },
        {
          "statement": "```python\nt = (4, 8, 15, 16)\nfor i in range(len(t)):\n    print(t[i])\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `4`, `8`, `15` puis `16`, une valeur par ligne",
              "correct": true,
              "feedback": "Bonne réponse : la lecture par indice\nfonctionne sur un tuple comme sur une\nliste. Seule l'écriture est interdite.\n"
            },
            {
              "text": "Il déclenche une exception, un tuple n'ayant pas d'indices",
              "correct": false,
              "feedback": "Erreur : les éléments d'un tuple sont\nnumérotés à partir de $0$, exactement\ncomme ceux d'une liste.\n"
            },
            {
              "text": "Il affiche `0`, `1`, `2` puis `3`, c'est-à-dire les indices",
              "correct": false,
              "feedback": "Erreur : `t[i]` désigne la valeur\nrangée à l'indice `i`, pas l'indice\nlui-même.\n"
            },
            {
              "text": "Il affiche `(4, 8, 15, 16)` en une seule ligne",
              "correct": false,
              "feedback": "Erreur : le `print` est à l'intérieur\nde la boucle, il s'exécute une fois\npar tour.\n"
            }
          ],
          "explanation": "Un tuple se lit exactement comme une\nliste : par indice, par parcours direct\n`for v in t`, ou avec `len`. La seule\ndifférence apparaît quand on veut\nécrire."
        },
        {
          "statement": "```python\nt = (7, 3)\nfor i in range(len(t)):\n    print(t[i])\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `7` puis `3`, une valeur par ligne",
              "correct": true,
              "feedback": "Bonne réponse : la lecture par indice\nfonctionne sur un tuple comme sur une\nliste. Seule l'écriture est interdite.\n"
            },
            {
              "text": "Il déclenche une exception, un tuple n'ayant pas d'indices",
              "correct": false,
              "feedback": "Erreur : les éléments d'un tuple sont\nnumérotés à partir de $0$, exactement\ncomme ceux d'une liste.\n"
            },
            {
              "text": "Il affiche `0` puis `1`, c'est-à-dire les indices",
              "correct": false,
              "feedback": "Erreur : `t[i]` désigne la valeur\nrangée à l'indice `i`, pas l'indice\nlui-même.\n"
            },
            {
              "text": "Il affiche `(7, 3)` en une seule ligne",
              "correct": false,
              "feedback": "Erreur : le `print` est à l'intérieur\nde la boucle, il s'exécute une fois\npar tour.\n"
            }
          ],
          "explanation": "Un tuple se lit exactement comme une\nliste : par indice, par parcours direct\n`for v in t`, ou avec `len`. La seule\ndifférence apparaît quand on veut\nécrire."
        }
      ]
    },
    {
      "id": "q34",
      "difficulty": 2,
      "skills": [
        "tuple",
        "concatenation",
        "immuabilite"
      ],
      "title": "Ajouter à un tuple",
      "partie": "tuples",
      "statement": "```python\nt = (1, 2)\nu = t + (3,)\nprint(t, u)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `(1, 2) (1, 2, 3)`",
          "correct": true,
          "feedback": "Bonne réponse : la concaténation\nconstruit un **nouveau** tuple `u`,\nsans toucher à `t`. C'est la seule\nfaçon d'« agrandir » un tuple.\n"
        },
        {
          "text": "Il affiche `(1, 2, 3) (1, 2, 3)`",
          "correct": false,
          "feedback": "Erreur : `t` n'est pas modifié, un\ntuple ne peut pas l'être. Seul `u`\ncontient la valeur $3$ en plus.\n"
        },
        {
          "text": "Il déclenche une exception, un tuple étant immuable",
          "correct": false,
          "feedback": "Erreur : l'immuabilité interdit de\nmodifier un tuple existant, pas d'en\ncréer un nouveau à partir de lui.\n"
        },
        {
          "text": "Il affiche `(1, 2) (1, 2, (3,))`",
          "correct": false,
          "feedback": "Erreur : le `+` entre deux tuples met\nleurs éléments bout à bout, il\nn'imbrique pas le second dans le\npremier.\n"
        }
      ],
      "explanation": "Retenir la nuance : on ne modifie jamais\nun tuple, on en fabrique un autre. Même\n`t += (3,)` ne fait pas exception, cette\nécriture range un nouveau tuple dans `t`.",
      "variants": [
        {
          "statement": "```python\nt = (1, 2)\nu = t + (3,)\nprint(t, u)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `(1, 2) (1, 2, 3)`",
              "correct": true,
              "feedback": "Bonne réponse : la concaténation\nconstruit un **nouveau** tuple `u`,\nsans toucher à `t`. C'est la seule\nfaçon d'« agrandir » un tuple.\n"
            },
            {
              "text": "Il affiche `(1, 2, 3) (1, 2, 3)`",
              "correct": false,
              "feedback": "Erreur : `t` n'est pas modifié, un\ntuple ne peut pas l'être. Seul `u`\ncontient la valeur $3$ en plus.\n"
            },
            {
              "text": "Il déclenche une exception, un tuple étant immuable",
              "correct": false,
              "feedback": "Erreur : l'immuabilité interdit de\nmodifier un tuple existant, pas d'en\ncréer un nouveau à partir de lui.\n"
            },
            {
              "text": "Il affiche `(1, 2) (1, 2, (3,))`",
              "correct": false,
              "feedback": "Erreur : le `+` entre deux tuples met\nleurs éléments bout à bout, il\nn'imbrique pas le second dans le\npremier.\n"
            }
          ],
          "explanation": "Retenir la nuance : on ne modifie jamais\nun tuple, on en fabrique un autre. Même\n`t += (3,)` ne fait pas exception, cette\nécriture range un nouveau tuple dans `t`."
        },
        {
          "statement": "```python\nt = (5, 6, 7)\nu = t + (8,)\nprint(t, u)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `(5, 6, 7) (5, 6, 7, 8)`",
              "correct": true,
              "feedback": "Bonne réponse : la concaténation\nconstruit un **nouveau** tuple `u`,\nsans toucher à `t`. C'est la seule\nfaçon d'« agrandir » un tuple.\n"
            },
            {
              "text": "Il affiche `(5, 6, 7, 8) (5, 6, 7, 8)`",
              "correct": false,
              "feedback": "Erreur : `t` n'est pas modifié, un\ntuple ne peut pas l'être. Seul `u`\ncontient la valeur $8$ en plus.\n"
            },
            {
              "text": "Il déclenche une exception, un tuple étant immuable",
              "correct": false,
              "feedback": "Erreur : l'immuabilité interdit de\nmodifier un tuple existant, pas d'en\ncréer un nouveau à partir de lui.\n"
            },
            {
              "text": "Il affiche `(5, 6, 7) (5, 6, 7, (8,))`",
              "correct": false,
              "feedback": "Erreur : le `+` entre deux tuples met\nleurs éléments bout à bout, il\nn'imbrique pas le second dans le\npremier.\n"
            }
          ],
          "explanation": "Retenir la nuance : on ne modifie jamais\nun tuple, on en fabrique un autre. Même\n`t += (8,)` ne fait pas exception, cette\nécriture range un nouveau tuple dans `t`."
        },
        {
          "statement": "```python\nt = (0,)\nu = t + (9,)\nprint(t, u)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `(0,) (0, 9)`",
              "correct": true,
              "feedback": "Bonne réponse : la concaténation\nconstruit un **nouveau** tuple `u`,\nsans toucher à `t`. C'est la seule\nfaçon d'« agrandir » un tuple.\n"
            },
            {
              "text": "Il affiche `(0, 9) (0, 9)`",
              "correct": false,
              "feedback": "Erreur : `t` n'est pas modifié, un\ntuple ne peut pas l'être. Seul `u`\ncontient la valeur $9$ en plus.\n"
            },
            {
              "text": "Il déclenche une exception, un tuple étant immuable",
              "correct": false,
              "feedback": "Erreur : l'immuabilité interdit de\nmodifier un tuple existant, pas d'en\ncréer un nouveau à partir de lui.\n"
            },
            {
              "text": "Il affiche `(0,) (0, (9,))`",
              "correct": false,
              "feedback": "Erreur : le `+` entre deux tuples met\nleurs éléments bout à bout, il\nn'imbrique pas le second dans le\npremier.\n"
            }
          ],
          "explanation": "Retenir la nuance : on ne modifie jamais\nun tuple, on en fabrique un autre. Même\n`t += (9,)` ne fait pas exception, cette\nécriture range un nouveau tuple dans `t`."
        }
      ]
    },
    {
      "id": "q35",
      "difficulty": 2,
      "skills": [
        "tuple",
        "parcours",
        "unpacking"
      ],
      "title": "Parcourir en déballant",
      "partie": "tuples",
      "statement": "```python\neleves = [(\"Ana\", 12), (\"Bilal\", 9)]\nfor prenom, note in eleves:\n    print(prenom, note)\n```\nPourquoi ce parcours fonctionne-t-il ?",
      "options": [
        {
          "text": "Chaque élément est un tuple déballé dans les deux variables",
          "correct": true,
          "feedback": "Bonne réponse : à chaque tour, le\ntuple courant est déballé, son premier\nélément va dans `prenom` et le second\ndans `note`.\n"
        },
        {
          "text": "Python parcourt d'abord tous les prénoms, puis toutes les notes",
          "correct": false,
          "feedback": "Erreur : la boucle avance élève par\nélève, et non colonne par colonne.\n"
        },
        {
          "text": "C'est une erreur : il faudrait écrire `for eleve in eleves` puis `eleve[0]`",
          "correct": false,
          "feedback": "Cette écriture fonctionne aussi, mais\nelle n'est pas obligatoire. Le\ndéballage donne des noms parlants aux\ndeux champs.\n"
        },
        {
          "text": "Cela ne fonctionne que si les deux valeurs sont du même type",
          "correct": false,
          "feedback": "Erreur : les types n'ont aucune\nimportance, seul le nombre de valeurs\ncompte.\n"
        }
      ],
      "explanation": "Le déballage dans l'en-tête de boucle\nrend le code lisible : `prenom` et `note`\ndisent ce que contiennent les deux\nchamps, là où `eleve[0]` et `eleve[1]`\nobligent à se souvenir de l'ordre."
    },
    {
      "id": "q36",
      "difficulty": 2,
      "skills": [
        "tuple",
        "immuabilite",
        "correction"
      ],
      "title": "Corriger une valeur d'un tuple",
      "partie": "tuples",
      "statement": "On dispose de `point = (3, 7)` et l'on\nveut obtenir le point d'abscisse $5$ et\nde même ordonnée. Que fait-on ?",
      "options": [
        {
          "text": "On construit un nouveau tuple : `point = (5, point[1])`",
          "correct": true,
          "feedback": "Bonne réponse : on ne modifie pas le\ntuple, on en construit un autre et on\nle range dans la même variable.\n"
        },
        {
          "text": "On écrit `point[0] = 5`",
          "correct": false,
          "feedback": "Erreur : cette affectation lève une\nexception `TypeError`, un tuple\nn'accepte aucune modification.\n"
        },
        {
          "text": "On écrit `point.modifier(0, 5)`",
          "correct": false,
          "feedback": "Erreur : cette méthode n'existe pas,\net modifier un tuple est de toute\nfaçon impossible.\n"
        },
        {
          "text": "On écrit `point.append(5)` puis on retire l'ancienne valeur",
          "correct": false,
          "feedback": "Erreur : `append` n'existe pas sur un\ntuple, et ajouter ne remplacerait pas\nl'abscisse.\n"
        }
      ],
      "explanation": "Quand une valeur doit changer souvent,\nc'est le signe qu'une liste conviendrait\nmieux. Le tuple s'emploie pour ce qui ne\nbouge pas, comme les coordonnées d'un\npoint déjà placé."
    },
    {
      "id": "q37",
      "difficulty": 3,
      "skills": [
        "tuple",
        "immuabilite",
        "aliasing-piege"
      ],
      "title": "Un tuple qui contient une liste",
      "partie": "tuples",
      "statement": "```python\nt = ([1, 2], 3)\nt[0].append(9)\nprint(t)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `([1, 2, 9], 3)`",
          "correct": true,
          "feedback": "Bonne réponse : le tuple garde les\nmêmes éléments, c'est la liste rangée\nen première position qui change. Rien\nn'interdit cela.\n"
        },
        {
          "text": "Il déclenche une exception, le tuple étant immuable",
          "correct": false,
          "feedback": "Erreur : on ne remplace aucun élément\ndu tuple, on modifie l'objet qu'il\ndésigne. Le tuple lui-même n'est pas\ntouché.\n"
        },
        {
          "text": "Il affiche `([1, 2], 3)`, la modification étant perdue",
          "correct": false,
          "feedback": "Erreur : `append` modifie bien la\nliste, et le tuple désigne cette même\nliste.\n"
        },
        {
          "text": "Il affiche `([1, 2], 3, 9)`",
          "correct": false,
          "feedback": "Erreur : le $9$ est ajouté à la liste\nintérieure, pas au tuple.\n"
        }
      ],
      "explanation": "L'immuabilité d'un tuple porte sur ses\ncases, pas sur le contenu des objets\nqu'elles désignent. Un tuple de listes\nreste donc modifiable en profondeur, ce\nqui lui interdit d'être clé de\ndictionnaire."
    },
    {
      "id": "q38",
      "difficulty": 3,
      "skills": [
        "tuple",
        "comparaison",
        "tri"
      ],
      "title": "Trier des tuples",
      "partie": "tuples",
      "statement": "Que vaut\n`sorted([(2, \"b\"), (1, \"z\"), (2, \"a\")])` ?",
      "options": [
        {
          "text": "`[(1, 'z'), (2, 'a'), (2, 'b')]`",
          "correct": true,
          "feedback": "Bonne réponse : deux tuples se\ncomparent élément par élément. Les\npremiers éléments décident, et en cas\nd'égalité on départage sur le\ndeuxième.\n"
        },
        {
          "text": "`[(1, 'z'), (2, 'b'), (2, 'a')]`",
          "correct": false,
          "feedback": "Erreur : à premier élément égal, la\ncomparaison se poursuit sur le\ndeuxième, donc `'a'` passe avant\n`'b'`.\n"
        },
        {
          "text": "`[(2, 'a'), (2, 'b'), (1, 'z')]`",
          "correct": false,
          "feedback": "Erreur : le tri est croissant, le\ntuple commençant par $1$ vient en\npremier.\n"
        },
        {
          "text": "Une exception, car les tuples mêlent nombres et chaînes",
          "correct": false,
          "feedback": "Erreur : la comparaison ne mêle jamais\nles types ici, elle compare des\nnombres entre eux puis, si besoin,\ndes chaînes entre elles.\n"
        }
      ],
      "explanation": "Cette comparaison par ordre lexical,\ncomme dans un dictionnaire de langue,\nexplique l'astuce courante : ranger la\nclé de tri en tête du tuple pour trier\ndes données sans effort."
    },
    {
      "id": "q39",
      "difficulty": 1,
      "skills": [
        "comparaison-tuple-liste",
        "choix"
      ],
      "title": "Un carnet de notes qui s'allonge",
      "partie": "choisir",
      "statement": "Un programme enregistre les notes d'un\nélève au fil de l'année. Quelle structure\nconvient ?",
      "options": [
        {
          "text": "Une liste, car le nombre de notes augmente au fil du temps",
          "correct": true,
          "feedback": "Bonne réponse : une structure qui\ngrandit demande une liste, seule à\naccepter `append`.\n"
        },
        {
          "text": "Un tuple, car les notes ne changent plus une fois attribuées",
          "correct": false,
          "feedback": "Une note déjà attribuée ne change\nplus, mais le carnet, lui, s'allonge :\nil faudrait reconstruire le tuple à\nchaque note.\n"
        },
        {
          "text": "Un tuple, car il occupe moins de place en mémoire",
          "correct": false,
          "feedback": "Le gain est réel mais négligeable ici,\net il ne compense pas l'impossibilité\nd'ajouter une note.\n"
        },
        {
          "text": "Une liste, mais seulement si les notes sont des entiers",
          "correct": false,
          "feedback": "Erreur : une liste accepte tous les\ntypes, entiers comme décimaux.\n"
        }
      ],
      "explanation": "La bonne question à se poser est\ntoujours la même : est-ce que le contenu\nva changer ? Si oui, liste ; si non,\ntuple."
    },
    {
      "id": "q40",
      "difficulty": 2,
      "skills": [
        "comparaison-tuple-liste",
        "liste-de-tuples"
      ],
      "title": "Un tableau de données",
      "partie": "choisir",
      "statement": "On veut représenter un tableau de\nrelevés, une ligne par mesure, chaque\nligne contenant une date et une\ntempérature. Quelle structure ?",
      "options": [
        {
          "text": "Une liste de tuples, la liste pour les lignes, un tuple par mesure",
          "correct": true,
          "feedback": "Bonne réponse : le tableau s'allonge à\nchaque relevé, d'où la liste ; une\nmesure, elle, forme un couple figé,\nd'où le tuple.\n"
        },
        {
          "text": "Un tuple de listes, le tuple pour les lignes, une liste par mesure",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse. Le tuple\nextérieur empêcherait d'ajouter des\nrelevés.\n"
        },
        {
          "text": "Une liste de listes, pour pouvoir modifier n'importe quelle valeur plus tard",
          "correct": false,
          "feedback": "Cela fonctionne, mais rien ne justifie\nde rendre modifiable une mesure déjà\nrelevée. Le tuple dit au lecteur que\nla ligne est figée.\n"
        },
        {
          "text": "Un tuple de tuples, pour empêcher toute modification du tableau de relevés",
          "correct": false,
          "feedback": "Erreur : le tableau doit accueillir de\nnouveaux relevés, il ne peut donc pas\nêtre un tuple.\n"
        }
      ],
      "explanation": "C'est la forme habituelle d'un tableau de\ndonnées en Python : une liste de lignes,\nchaque ligne étant un tuple de champs,\nexactement ce que renvoie la lecture d'un\nfichier CSV."
    },
    {
      "id": "q41",
      "difficulty": 2,
      "skills": [
        "tuple",
        "tranche"
      ],
      "title": "Tranche d'un tuple",
      "partie": "tuples",
      "statement": "On dispose de `t = (10, 20, 30, 40, 50)`.\nQue vaut `t[1:3]` ?",
      "options": [
        {
          "text": "Le tuple `(20, 30)`",
          "correct": true,
          "feedback": "Bonne réponse : la tranche part de\nl'indice $1$ inclus et s'arrête à\nl'indice $3$ exclu. Le résultat est un\nnouveau tuple, celui d'origine reste\nintact.\n"
        },
        {
          "text": "La liste `[20, 30]`, car une tranche renvoie toujours une liste",
          "correct": false,
          "feedback": "Erreur : une tranche conserve le type\nde départ. Sur un tuple, elle renvoie\nun tuple.\n"
        },
        {
          "text": "Le tuple `(20, 30, 40)`, les deux indices étant inclus",
          "correct": false,
          "feedback": "Erreur : l'indice de fin est exclu, le\n$40$ n'est pas pris.\n"
        },
        {
          "text": "Une exception, un tuple n'acceptant pas les tranches",
          "correct": false,
          "feedback": "Erreur : lire une tranche ne modifie\nrien, c'est donc permis sur un tuple.\n"
        }
      ],
      "explanation": "Découper ne modifie pas : la tranche\nconstruit une nouvelle structure du même\ntype que l'original. `t[1:3]` se lit « de\nl'indice $1$ jusqu'à l'indice $3$ exclu »."
    },
    {
      "id": "q42",
      "difficulty": 2,
      "skills": [
        "liste",
        "remove",
        "suppression"
      ],
      "title": "Retirer une valeur d'une liste",
      "partie": "listes",
      "statement": "```python\nl = [3, 1, 4, 1, 5]\nl.remove(1)\nprint(l)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `[3, 4, 1, 5]`",
          "correct": true,
          "feedback": "Bonne réponse : `remove` retire la\n**première** occurrence de la valeur\ndemandée, pas toutes. Le second $1$\nreste en place.\n"
        },
        {
          "text": "Il affiche `[3, 4, 5]`, les deux $1$ ayant disparu",
          "correct": false,
          "feedback": "Erreur : une seule occurrence est\nretirée par appel. Il faudrait appeler\n`remove` autant de fois qu'il y a de\nvaleurs à retirer.\n"
        },
        {
          "text": "Il affiche `[1, 4, 1, 5]`, le premier élément étant retiré",
          "correct": false,
          "feedback": "Erreur : `remove` prend une **valeur**,\npas une position. Retirer par indice se\nfait autrement.\n"
        },
        {
          "text": "Il affiche `[3, 1, 4, 1, 5]`, `remove` renvoyant une nouvelle liste",
          "correct": false,
          "feedback": "Erreur : `remove` modifie la liste sur\nplace et ne renvoie rien.\n"
        }
      ],
      "explanation": "Deux pièges en un : `remove` travaille sur\nune valeur et non sur un indice, et il\nn'en retire qu'une seule. Sur un tuple,\ncette méthode n'existe pas, puisqu'elle\nmodifierait la structure.",
      "variants": [
        {
          "statement": "```python\nl = [3, 1, 4, 1, 5]\nl.remove(1)\nprint(l)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `[3, 4, 1, 5]`",
              "correct": true,
              "feedback": "Bonne réponse : `remove` retire la\n**première** occurrence de la valeur\ndemandée, pas toutes. Le second $1$\nreste en place.\n"
            },
            {
              "text": "Il affiche `[3, 4, 5]`, les deux $1$ ayant disparu",
              "correct": false,
              "feedback": "Erreur : une seule occurrence est\nretirée par appel. Il faudrait appeler\n`remove` autant de fois qu'il y a de\nvaleurs à retirer.\n"
            },
            {
              "text": "Il affiche `[1, 4, 1, 5]`, le premier élément étant retiré",
              "correct": false,
              "feedback": "Erreur : `remove` prend une **valeur**,\npas une position. Retirer par indice se\nfait autrement.\n"
            },
            {
              "text": "Il affiche `[3, 1, 4, 1, 5]`, `remove` renvoyant une nouvelle liste",
              "correct": false,
              "feedback": "Erreur : `remove` modifie la liste sur\nplace et ne renvoie rien.\n"
            }
          ],
          "explanation": "Deux pièges en un : `remove` travaille sur\nune valeur et non sur un indice, et il\nn'en retire qu'une seule. Sur un tuple,\ncette méthode n'existe pas, puisqu'elle\nmodifierait la structure."
        },
        {
          "statement": "```python\nl = [2, 8, 5, 8, 6]\nl.remove(8)\nprint(l)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `[2, 5, 8, 6]`",
              "correct": true,
              "feedback": "Bonne réponse : `remove` retire la\n**première** occurrence de la valeur\ndemandée, pas toutes. Le second $8$\nreste en place.\n"
            },
            {
              "text": "Il affiche `[2, 5, 6]`, les deux $8$ ayant disparu",
              "correct": false,
              "feedback": "Erreur : une seule occurrence est\nretirée par appel. Il faudrait appeler\n`remove` autant de fois qu'il y a de\nvaleurs à retirer.\n"
            },
            {
              "text": "Il affiche `[8, 5, 8, 6]`, le premier élément étant retiré",
              "correct": false,
              "feedback": "Erreur : `remove` prend une **valeur**,\npas une position. Retirer par indice se\nfait autrement.\n"
            },
            {
              "text": "Il affiche `[2, 8, 5, 8, 6]`, `remove` renvoyant une nouvelle liste",
              "correct": false,
              "feedback": "Erreur : `remove` modifie la liste sur\nplace et ne renvoie rien.\n"
            }
          ],
          "explanation": "Deux pièges en un : `remove` travaille sur\nune valeur et non sur un indice, et il\nn'en retire qu'une seule. Sur un tuple,\ncette méthode n'existe pas, puisqu'elle\nmodifierait la structure."
        },
        {
          "statement": "```python\nl = [4, 7, 9, 7, 2]\nl.remove(7)\nprint(l)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `[4, 9, 7, 2]`",
              "correct": true,
              "feedback": "Bonne réponse : `remove` retire la\n**première** occurrence de la valeur\ndemandée, pas toutes. Le second $7$\nreste en place.\n"
            },
            {
              "text": "Il affiche `[4, 9, 2]`, les deux $7$ ayant disparu",
              "correct": false,
              "feedback": "Erreur : une seule occurrence est\nretirée par appel. Il faudrait appeler\n`remove` autant de fois qu'il y a de\nvaleurs à retirer.\n"
            },
            {
              "text": "Il affiche `[7, 9, 7, 2]`, le premier élément étant retiré",
              "correct": false,
              "feedback": "Erreur : `remove` prend une **valeur**,\npas une position. Retirer par indice se\nfait autrement.\n"
            },
            {
              "text": "Il affiche `[4, 7, 9, 7, 2]`, `remove` renvoyant une nouvelle liste",
              "correct": false,
              "feedback": "Erreur : `remove` modifie la liste sur\nplace et ne renvoie rien.\n"
            }
          ],
          "explanation": "Deux pièges en un : `remove` travaille sur\nune valeur et non sur un indice, et il\nn'en retire qu'une seule. Sur un tuple,\ncette méthode n'existe pas, puisqu'elle\nmodifierait la structure."
        }
      ]
    },
    {
      "id": "q43",
      "difficulty": 1,
      "skills": [
        "liste",
        "longueur",
        "indices"
      ],
      "title": "Indice du dernier élément",
      "partie": "listes",
      "statement": "La liste `notes` contient $12$ valeurs.\nQuelle expression désigne sa dernière\nvaleur ?",
      "options": [
        {
          "text": "`notes[len(notes) - 1]`",
          "correct": true,
          "feedback": "Bonne réponse : les indices vont de $0$\nà $11$, le dernier vaut donc\n$12 - 1 = 11$.\n"
        },
        {
          "text": "`notes[len(notes)]`, la longueur servant d'indice",
          "correct": false,
          "feedback": "Erreur : cet indice vaut $12$, il\ndépasse d'une unité et lève une\nexception `IndexError`.\n"
        },
        {
          "text": "`notes[12]`, puisque la liste compte douze valeurs",
          "correct": false,
          "feedback": "Erreur : même problème, les indices\ns'arrêtent à $11$.\n"
        },
        {
          "text": "`notes.dernier()`",
          "correct": false,
          "feedback": "Erreur : cette méthode n'existe pas en\nPython.\n"
        }
      ],
      "explanation": "Une liste de $n$ éléments a ses indices de\n$0$ à $n-1$ : c'est la source d'erreur la\nplus fréquente quand on parcourt une liste\npar ses indices."
    },
    {
      "id": "q44",
      "difficulty": 1,
      "skills": [
        "liste",
        "construction-liste",
        "append"
      ],
      "title": "Construire une liste dans une boucle",
      "partie": "listes",
      "statement": "```python\ncarres = []\nfor i in range(1, 5):\n    carres.append(i * i)\nprint(carres)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `[1, 4, 9, 16]`",
          "correct": true,
          "feedback": "Bonne réponse : la liste part vide et\nreçoit un carré à chaque tour, pour `i`\nvalant $1$, $2$, $3$ puis $4$.\n"
        },
        {
          "text": "Il affiche `[1, 4, 9, 16, 25]`",
          "correct": false,
          "feedback": "Erreur : `range(1, 5)` s'arrête à $4$,\nla borne de fin est exclue.\n"
        },
        {
          "text": "Il affiche `[0, 1, 4, 9, 16]`",
          "correct": false,
          "feedback": "Erreur : la boucle commence à $1$, pas\nà $0$.\n"
        },
        {
          "text": "Il affiche `16`, seule la dernière valeur étant gardée",
          "correct": false,
          "feedback": "Erreur : `append` ajoute sans écraser,\ntoutes les valeurs restent.\n"
        }
      ],
      "explanation": "C'est le schéma le plus courant en\nprogrammation : une liste vide, une boucle,\nun `append` par tour. Le résultat se lit\ndans l'ordre de construction.",
      "variants": [
        {
          "statement": "```python\ncarres = []\nfor i in range(1, 5):\n    carres.append(i * i)\nprint(carres)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `[1, 4, 9, 16]`",
              "correct": true,
              "feedback": "Bonne réponse : la liste part vide et\nreçoit un carré à chaque tour, pour `i`\nvalant $1$, $2$, $3$ puis $4$.\n"
            },
            {
              "text": "Il affiche `[1, 4, 9, 16, 25]`",
              "correct": false,
              "feedback": "Erreur : `range(1, 5)` s'arrête à $4$,\nla borne de fin est exclue.\n"
            },
            {
              "text": "Il affiche `[0, 1, 4, 9, 16]`",
              "correct": false,
              "feedback": "Erreur : la boucle commence à $1$, pas\nà $0$.\n"
            },
            {
              "text": "Il affiche `16`, seule la dernière valeur étant gardée",
              "correct": false,
              "feedback": "Erreur : `append` ajoute sans écraser,\ntoutes les valeurs restent.\n"
            }
          ],
          "explanation": "C'est le schéma le plus courant en\nprogrammation : une liste vide, une boucle,\nun `append` par tour. Le résultat se lit\ndans l'ordre de construction."
        },
        {
          "statement": "```python\ncarres = []\nfor i in range(3, 7):\n    carres.append(i * i)\nprint(carres)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `[9, 16, 25, 36]`",
              "correct": true,
              "feedback": "Bonne réponse : la liste part vide et\nreçoit un carré à chaque tour, pour `i`\nvalant $3$, $4$, $5$ puis $6$.\n"
            },
            {
              "text": "Il affiche `[9, 16, 25, 36, 49]`",
              "correct": false,
              "feedback": "Erreur : `range(3, 7)` s'arrête à $6$,\nla borne de fin est exclue.\n"
            },
            {
              "text": "Il affiche `[4, 9, 16, 25, 36]`",
              "correct": false,
              "feedback": "Erreur : la boucle commence à $3$, pas\nà $2$.\n"
            },
            {
              "text": "Il affiche `36`, seule la dernière valeur étant gardée",
              "correct": false,
              "feedback": "Erreur : `append` ajoute sans écraser,\ntoutes les valeurs restent.\n"
            }
          ],
          "explanation": "C'est le schéma le plus courant en\nprogrammation : une liste vide, une boucle,\nun `append` par tour. Le résultat se lit\ndans l'ordre de construction."
        },
        {
          "statement": "```python\ncarres = []\nfor i in range(2, 5):\n    carres.append(i * i)\nprint(carres)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `[4, 9, 16]`",
              "correct": true,
              "feedback": "Bonne réponse : la liste part vide et\nreçoit un carré à chaque tour, pour `i`\nvalant $2$, $3$ puis $4$.\n"
            },
            {
              "text": "Il affiche `[4, 9, 16, 25]`",
              "correct": false,
              "feedback": "Erreur : `range(2, 5)` s'arrête à $4$,\nla borne de fin est exclue.\n"
            },
            {
              "text": "Il affiche `[1, 4, 9, 16]`",
              "correct": false,
              "feedback": "Erreur : la boucle commence à $2$, pas\nà $1$.\n"
            },
            {
              "text": "Il affiche `16`, seule la dernière valeur étant gardée",
              "correct": false,
              "feedback": "Erreur : `append` ajoute sans écraser,\ntoutes les valeurs restent.\n"
            }
          ],
          "explanation": "C'est le schéma le plus courant en\nprogrammation : une liste vide, une boucle,\nun `append` par tour. Le résultat se lit\ndans l'ordre de construction."
        }
      ]
    },
    {
      "id": "q45",
      "difficulty": 1,
      "skills": [
        "liste",
        "range",
        "conversion"
      ],
      "title": "D'un range à une liste",
      "partie": "listes",
      "statement": "Que vaut `list(range(2, 9, 2))` ?",
      "options": [
        {
          "text": "`[2, 4, 6, 8]`",
          "correct": true,
          "feedback": "Bonne réponse : on part de $2$, on\navance de $2$ en $2$, et on s'arrête\navant $9$.\n"
        },
        {
          "text": "`[2, 4, 6, 8, 10]`",
          "correct": false,
          "feedback": "Erreur : $10$ dépasse la borne de fin,\nqui est exclue.\n"
        },
        {
          "text": "`[2, 3, 4, 5, 6, 7, 8]`",
          "correct": false,
          "feedback": "Erreur : le troisième argument est le\npas, ici $2$, et non une simple borne.\n"
        },
        {
          "text": "`[0, 2, 4, 6, 8]`",
          "correct": false,
          "feedback": "Erreur : le premier argument est le\ndébut, ici $2$ et non $0$.\n"
        }
      ],
      "explanation": "`range` ne crée pas une liste mais une\nsuite d'entiers parcourable ; `list` la\ndéroule. Les trois arguments se lisent\n« début, fin exclue, pas ».",
      "variants": [
        {
          "statement": "Que vaut `list(range(2, 9, 2))` ?",
          "options": [
            {
              "text": "`[2, 4, 6, 8]`",
              "correct": true,
              "feedback": "Bonne réponse : on part de $2$, on\navance de $2$ en $2$, et on s'arrête\navant $9$.\n"
            },
            {
              "text": "`[2, 4, 6, 8, 10]`",
              "correct": false,
              "feedback": "Erreur : $10$ dépasse la borne de fin,\nqui est exclue.\n"
            },
            {
              "text": "`[2, 3, 4, 5, 6, 7, 8]`",
              "correct": false,
              "feedback": "Erreur : le troisième argument est le\npas, ici $2$, et non une simple borne.\n"
            },
            {
              "text": "`[0, 2, 4, 6, 8]`",
              "correct": false,
              "feedback": "Erreur : le premier argument est le\ndébut, ici $2$ et non $0$.\n"
            }
          ],
          "explanation": "`range` ne crée pas une liste mais une\nsuite d'entiers parcourable ; `list` la\ndéroule. Les trois arguments se lisent\n« début, fin exclue, pas »."
        },
        {
          "statement": "Que vaut `list(range(1, 10, 3))` ?",
          "options": [
            {
              "text": "`[1, 4, 7]`",
              "correct": true,
              "feedback": "Bonne réponse : on part de $1$, on\navance de $3$ en $3$, et on s'arrête\navant $10$.\n"
            },
            {
              "text": "`[1, 4, 7, 10]`",
              "correct": false,
              "feedback": "Erreur : $10$ dépasse la borne de fin,\nqui est exclue.\n"
            },
            {
              "text": "`[1, 2, 3, 4, 5, 6, 7, 8, 9]`",
              "correct": false,
              "feedback": "Erreur : le troisième argument est le\npas, ici $3$, et non une simple borne.\n"
            },
            {
              "text": "`[0, 3, 6, 9]`",
              "correct": false,
              "feedback": "Erreur : le premier argument est le\ndébut, ici $1$ et non $0$.\n"
            }
          ],
          "explanation": "`range` ne crée pas une liste mais une\nsuite d'entiers parcourable ; `list` la\ndéroule. Les trois arguments se lisent\n« début, fin exclue, pas »."
        },
        {
          "statement": "Que vaut `list(range(3, 13, 4))` ?",
          "options": [
            {
              "text": "`[3, 7, 11]`",
              "correct": true,
              "feedback": "Bonne réponse : on part de $3$, on\navance de $4$ en $4$, et on s'arrête\navant $13$.\n"
            },
            {
              "text": "`[3, 7, 11, 15]`",
              "correct": false,
              "feedback": "Erreur : $15$ dépasse la borne de fin,\nqui est exclue.\n"
            },
            {
              "text": "`[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]`",
              "correct": false,
              "feedback": "Erreur : le troisième argument est le\npas, ici $4$, et non une simple borne.\n"
            },
            {
              "text": "`[0, 4, 8, 12]`",
              "correct": false,
              "feedback": "Erreur : le premier argument est le\ndébut, ici $3$ et non $0$.\n"
            }
          ],
          "explanation": "`range` ne crée pas une liste mais une\nsuite d'entiers parcourable ; `list` la\ndéroule. Les trois arguments se lisent\n« début, fin exclue, pas »."
        }
      ]
    },
    {
      "id": "q46",
      "difficulty": 2,
      "skills": [
        "liste",
        "parcours",
        "indices"
      ],
      "title": "Deux façons de parcourir",
      "partie": "listes",
      "statement": "```python\nl = [4, 7, 2]\nfor v in l:\n    print(v)\nfor i in range(len(l)):\n    print(l[i])\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche deux fois de suite les valeurs $4$, $7$ et $2$",
          "correct": true,
          "feedback": "Bonne réponse : les deux parcours\ndonnent les mêmes valeurs, dans le même\nordre. Le second connaît en plus la\nposition de chaque élément.\n"
        },
        {
          "text": "Il affiche les valeurs, puis les indices $0$, $1$ et $2$",
          "correct": false,
          "feedback": "Erreur : la seconde boucle affiche\n`l[i]`, c'est-à-dire la valeur rangée à\nl'indice `i`.\n"
        },
        {
          "text": "Il déclenche une exception à la seconde boucle",
          "correct": false,
          "feedback": "Erreur : `range(len(l))` produit\nexactement les indices valides.\n"
        },
        {
          "text": "Il affiche les valeurs, puis la liste entière trois fois",
          "correct": false,
          "feedback": "Erreur : `l[i]` désigne un élément, pas\nla liste.\n"
        }
      ],
      "explanation": "Parcourir par élément se lit mieux ;\nparcourir par indice devient nécessaire dès\nqu'on a besoin de la position, par exemple\npour comparer un élément à son voisin.",
      "variants": [
        {
          "statement": "```python\nl = [4, 7, 2]\nfor v in l:\n    print(v)\nfor i in range(len(l)):\n    print(l[i])\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche deux fois de suite les valeurs $4$, $7$ et $2$",
              "correct": true,
              "feedback": "Bonne réponse : les deux parcours\ndonnent les mêmes valeurs, dans le même\nordre. Le second connaît en plus la\nposition de chaque élément.\n"
            },
            {
              "text": "Il affiche les valeurs, puis les indices $0$, $1$ et $2$",
              "correct": false,
              "feedback": "Erreur : la seconde boucle affiche\n`l[i]`, c'est-à-dire la valeur rangée à\nl'indice `i`.\n"
            },
            {
              "text": "Il déclenche une exception à la seconde boucle",
              "correct": false,
              "feedback": "Erreur : `range(len(l))` produit\nexactement les indices valides.\n"
            },
            {
              "text": "Il affiche les valeurs, puis la liste entière trois fois",
              "correct": false,
              "feedback": "Erreur : `l[i]` désigne un élément, pas\nla liste.\n"
            }
          ],
          "explanation": "Parcourir par élément se lit mieux ;\nparcourir par indice devient nécessaire dès\nqu'on a besoin de la position, par exemple\npour comparer un élément à son voisin."
        },
        {
          "statement": "```python\nl = [9, 1, 5, 3]\nfor v in l:\n    print(v)\nfor i in range(len(l)):\n    print(l[i])\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche deux fois de suite les valeurs $9$, $1$, $5$ et $3$",
              "correct": true,
              "feedback": "Bonne réponse : les deux parcours\ndonnent les mêmes valeurs, dans le même\nordre. Le second connaît en plus la\nposition de chaque élément.\n"
            },
            {
              "text": "Il affiche les valeurs, puis les indices $0$, $1$, $2$ et $3$",
              "correct": false,
              "feedback": "Erreur : la seconde boucle affiche\n`l[i]`, c'est-à-dire la valeur rangée à\nl'indice `i`.\n"
            },
            {
              "text": "Il déclenche une exception à la seconde boucle",
              "correct": false,
              "feedback": "Erreur : `range(len(l))` produit\nexactement les indices valides.\n"
            },
            {
              "text": "Il affiche les valeurs, puis la liste entière quatre fois",
              "correct": false,
              "feedback": "Erreur : `l[i]` désigne un élément, pas\nla liste.\n"
            }
          ],
          "explanation": "Parcourir par élément se lit mieux ;\nparcourir par indice devient nécessaire dès\nqu'on a besoin de la position, par exemple\npour comparer un élément à son voisin."
        },
        {
          "statement": "```python\nl = [6, 8]\nfor v in l:\n    print(v)\nfor i in range(len(l)):\n    print(l[i])\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche deux fois de suite les valeurs $6$ et $8$",
              "correct": true,
              "feedback": "Bonne réponse : les deux parcours\ndonnent les mêmes valeurs, dans le même\nordre. Le second connaît en plus la\nposition de chaque élément.\n"
            },
            {
              "text": "Il affiche les valeurs, puis les indices $0$ et $1$",
              "correct": false,
              "feedback": "Erreur : la seconde boucle affiche\n`l[i]`, c'est-à-dire la valeur rangée à\nl'indice `i`.\n"
            },
            {
              "text": "Il déclenche une exception à la seconde boucle",
              "correct": false,
              "feedback": "Erreur : `range(len(l))` produit\nexactement les indices valides.\n"
            },
            {
              "text": "Il affiche les valeurs, puis la liste entière deux fois",
              "correct": false,
              "feedback": "Erreur : `l[i]` désigne un élément, pas\nla liste.\n"
            }
          ],
          "explanation": "Parcourir par élément se lit mieux ;\nparcourir par indice devient nécessaire dès\nqu'on a besoin de la position, par exemple\npour comparer un élément à son voisin."
        }
      ]
    },
    {
      "id": "q47",
      "difficulty": 2,
      "skills": [
        "liste",
        "accumulateur",
        "somme"
      ],
      "title": "Somme des éléments",
      "partie": "listes",
      "statement": "```python\nnotes = [12, 15, 9]\ntotal = 0\nfor n in notes:\n    total = total + n\nprint(total)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `36`",
          "correct": true,
          "feedback": "Bonne réponse : l'accumulateur part de\n$0$ et reçoit chaque note, d'où\n$12 + 15 + 9 = 36$.\n"
        },
        {
          "text": "Il affiche `12`",
          "correct": false,
          "feedback": "Erreur : ce serait le résultat si\n`total` était réaffecté à chaque tour\nau lieu d'être augmenté.\n"
        },
        {
          "text": "Il affiche `[12, 15, 9]`",
          "correct": false,
          "feedback": "Erreur : `total` est un nombre, pas une\nliste.\n"
        },
        {
          "text": "Il affiche `9`",
          "correct": false,
          "feedback": "Erreur : $9$ est la dernière note, pas\nla somme.\n"
        }
      ],
      "explanation": "L'accumulateur initialisé à $0$ avant la\nboucle est le schéma de base d'un calcul\nsur une liste : somme, produit, moyenne,\ncomptage en dérivent tous.",
      "variants": [
        {
          "statement": "```python\nnotes = [12, 15, 9]\ntotal = 0\nfor n in notes:\n    total = total + n\nprint(total)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `36`",
              "correct": true,
              "feedback": "Bonne réponse : l'accumulateur part de\n$0$ et reçoit chaque note, d'où\n$12 + 15 + 9 = 36$.\n"
            },
            {
              "text": "Il affiche `12`",
              "correct": false,
              "feedback": "Erreur : ce serait le résultat si\n`total` était réaffecté à chaque tour\nau lieu d'être augmenté.\n"
            },
            {
              "text": "Il affiche `[12, 15, 9]`",
              "correct": false,
              "feedback": "Erreur : `total` est un nombre, pas une\nliste.\n"
            },
            {
              "text": "Il affiche `9`",
              "correct": false,
              "feedback": "Erreur : $9$ est la dernière note, pas\nla somme.\n"
            }
          ],
          "explanation": "L'accumulateur initialisé à $0$ avant la\nboucle est le schéma de base d'un calcul\nsur une liste : somme, produit, moyenne,\ncomptage en dérivent tous."
        },
        {
          "statement": "```python\nnotes = [8, 14, 11, 17]\ntotal = 0\nfor n in notes:\n    total = total + n\nprint(total)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `50`",
              "correct": true,
              "feedback": "Bonne réponse : l'accumulateur part de\n$0$ et reçoit chaque note, d'où\n$8 + 14 + 11 + 17 = 50$.\n"
            },
            {
              "text": "Il affiche `8`",
              "correct": false,
              "feedback": "Erreur : ce serait le résultat si\n`total` était réaffecté à chaque tour\nau lieu d'être augmenté.\n"
            },
            {
              "text": "Il affiche `[8, 14, 11, 17]`",
              "correct": false,
              "feedback": "Erreur : `total` est un nombre, pas une\nliste.\n"
            },
            {
              "text": "Il affiche `17`",
              "correct": false,
              "feedback": "Erreur : $17$ est la dernière note, pas\nla somme.\n"
            }
          ],
          "explanation": "L'accumulateur initialisé à $0$ avant la\nboucle est le schéma de base d'un calcul\nsur une liste : somme, produit, moyenne,\ncomptage en dérivent tous."
        },
        {
          "statement": "```python\nnotes = [20, 5]\ntotal = 0\nfor n in notes:\n    total = total + n\nprint(total)\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `25`",
              "correct": true,
              "feedback": "Bonne réponse : l'accumulateur part de\n$0$ et reçoit chaque note, d'où\n$20 + 5 = 25$.\n"
            },
            {
              "text": "Il affiche `20`",
              "correct": false,
              "feedback": "Erreur : ce serait le résultat si\n`total` était réaffecté à chaque tour\nau lieu d'être augmenté.\n"
            },
            {
              "text": "Il affiche `[20, 5]`",
              "correct": false,
              "feedback": "Erreur : `total` est un nombre, pas une\nliste.\n"
            },
            {
              "text": "Il affiche `5`",
              "correct": false,
              "feedback": "Erreur : $5$ est la dernière note, pas\nla somme.\n"
            }
          ],
          "explanation": "L'accumulateur initialisé à $0$ avant la\nboucle est le schéma de base d'un calcul\nsur une liste : somme, produit, moyenne,\ncomptage en dérivent tous."
        }
      ]
    },
    {
      "id": "q48",
      "difficulty": 2,
      "skills": [
        "liste",
        "maximum",
        "parcours"
      ],
      "title": "Chercher le maximum",
      "partie": "listes",
      "statement": "```python\nvaleurs = [3, 9, 4]\nm = valeurs[0]\nfor v in valeurs:\n    if v > m:\n        m = v\nprint(m)\n```\nPourquoi initialise-t-on `m` avec\n`valeurs[0]` ?",
      "options": [
        {
          "text": "Pour partir d'une valeur de la liste",
          "correct": true,
          "feedback": "Bonne réponse : en partant du premier\nélément, la comparaison a du sens dès le\npremier tour, quelles que soient les\nvaleurs, y compris négatives.\n"
        },
        {
          "text": "Parce que le maximum est toujours le premier élément",
          "correct": false,
          "feedback": "Erreur : ici le maximum est $9$, qui\nn'est pas en première position.\n"
        },
        {
          "text": "Parce qu'une variable doit toujours valoir $0$ au départ",
          "correct": false,
          "feedback": "Erreur : initialiser à $0$ donnerait un\nrésultat faux si toutes les valeurs\nétaient négatives.\n"
        },
        {
          "text": "Pour que la boucle commence à l'indice $1$",
          "correct": false,
          "feedback": "Erreur : la boucle parcourt toute la\nliste, le premier élément est\nsimplement comparé à lui-même.\n"
        }
      ],
      "explanation": "Initialiser avec un élément de la liste,\nplutôt qu'avec $0$, est la précaution qui\nrend l'algorithme correct pour des valeurs\nnégatives."
    },
    {
      "id": "q49",
      "difficulty": 2,
      "skills": [
        "liste",
        "comprehension-liste",
        "filtre"
      ],
      "title": "Compréhension avec condition",
      "partie": "listes",
      "statement": "On dispose de `notes = [8, 14, 11, 5]`.\nQuelle expression construit la liste des\nnotes supérieures ou égales à $10$ ?",
      "options": [
        {
          "text": "`[n for n in notes if n >= 10]`",
          "correct": true,
          "feedback": "Bonne réponse : la condition filtre les\néléments gardés, ici `[14, 11]`.\n"
        },
        {
          "text": "`[n if n >= 10 for n in notes]`",
          "correct": false,
          "feedback": "Erreur : la condition de filtre se place\naprès la boucle, pas avant.\n"
        },
        {
          "text": "`[notes if n >= 10]`, sans boucle explicite",
          "correct": false,
          "feedback": "Erreur : il manque la boucle `for`, qui\nparcourt les éléments un à un.\n"
        },
        {
          "text": "`[n >= 10 for n in notes]`, qui teste chaque note",
          "correct": false,
          "feedback": "Erreur : cette expression construit une\nliste de booléens, `[False, True, True,\nFalse]`, et non les notes elles-mêmes.\n"
        }
      ],
      "explanation": "Une compréhension se lit dans l'ordre :\nce qu'on garde, d'où ça vient, à quelle\ncondition. Elle remplace une boucle avec\n`append` en une ligne."
    },
    {
      "id": "q50",
      "difficulty": 2,
      "skills": [
        "liste",
        "listes-imbriquees",
        "tableau"
      ],
      "title": "Tableau à deux dimensions",
      "partie": "listes",
      "statement": "```python\ngrille = [[1, 2, 3], [4, 5, 6]]\nprint(grille[1][2])\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `6`",
          "correct": true,
          "feedback": "Bonne réponse : `grille[1]` désigne la\ndeuxième ligne `[4, 5, 6]`, et `[2]` en\nprend le troisième élément.\n"
        },
        {
          "text": "Il affiche `3`",
          "correct": false,
          "feedback": "Erreur : ce serait `grille[0][2]`, sur\nune autre ligne.\n"
        },
        {
          "text": "Il affiche `[4, 5, 6]`",
          "correct": false,
          "feedback": "Erreur : le second indice descend dans\nla ligne, il ne l'affiche pas en\nentier.\n"
        },
        {
          "text": "Il affiche `2`, l'élément d'indice $1$ dans la liste aplatie",
          "correct": false,
          "feedback": "Erreur : une liste de listes n'est pas\naplatie, les deux indices désignent la\nligne puis la colonne.\n"
        }
      ],
      "explanation": "Un tableau de données se range souvent\nainsi : une liste de lignes, chaque ligne\nétant une liste de valeurs. Le premier\nindice choisit la ligne, le second la\ncolonne.",
      "variants": [
        {
          "statement": "```python\ngrille = [[1, 2, 3], [4, 5, 6]]\nprint(grille[1][2])\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `6`",
              "correct": true,
              "feedback": "Bonne réponse : `grille[1]` désigne la\ndeuxième ligne `[4, 5, 6]`, et `[2]` en\nprend le troisième élément.\n"
            },
            {
              "text": "Il affiche `3`",
              "correct": false,
              "feedback": "Erreur : ce serait `grille[0][2]`, sur\nune autre ligne.\n"
            },
            {
              "text": "Il affiche `[4, 5, 6]`",
              "correct": false,
              "feedback": "Erreur : le second indice descend dans\nla ligne, il ne l'affiche pas en\nentier.\n"
            },
            {
              "text": "Il affiche `2`, l'élément d'indice $1$ dans la liste aplatie",
              "correct": false,
              "feedback": "Erreur : une liste de listes n'est pas\naplatie, les deux indices désignent la\nligne puis la colonne.\n"
            }
          ],
          "explanation": "Un tableau de données se range souvent\nainsi : une liste de lignes, chaque ligne\nétant une liste de valeurs. Le premier\nindice choisit la ligne, le second la\ncolonne."
        },
        {
          "statement": "```python\ngrille = [[7, 8], [9, 10], [11, 12]]\nprint(grille[2][1])\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `12`",
              "correct": true,
              "feedback": "Bonne réponse : `grille[2]` désigne la\ntroisième ligne `[11, 12]`, et `[1]` en\nprend le deuxième élément.\n"
            },
            {
              "text": "Il affiche `8`",
              "correct": false,
              "feedback": "Erreur : ce serait `grille[0][1]`, sur\nune autre ligne.\n"
            },
            {
              "text": "Il affiche `[11, 12]`",
              "correct": false,
              "feedback": "Erreur : le second indice descend dans\nla ligne, il ne l'affiche pas en\nentier.\n"
            },
            {
              "text": "Il affiche `9`, l'élément d'indice $2$ dans la liste aplatie",
              "correct": false,
              "feedback": "Erreur : une liste de listes n'est pas\naplatie, les deux indices désignent la\nligne puis la colonne.\n"
            }
          ],
          "explanation": "Un tableau de données se range souvent\nainsi : une liste de lignes, chaque ligne\nétant une liste de valeurs. Le premier\nindice choisit la ligne, le second la\ncolonne."
        },
        {
          "statement": "```python\ngrille = [[0, 1, 2, 3], [4, 5, 6, 7]]\nprint(grille[0][3])\n```\nQue produit ce code ?",
          "options": [
            {
              "text": "Il affiche `3`",
              "correct": true,
              "feedback": "Bonne réponse : `grille[0]` désigne la\npremière ligne `[0, 1, 2, 3]`, et `[3]` en\nprend le quatrième élément.\n"
            },
            {
              "text": "Il affiche `7`",
              "correct": false,
              "feedback": "Erreur : ce serait `grille[1][3]`, sur\nune autre ligne.\n"
            },
            {
              "text": "Il affiche `[0, 1, 2, 3]`",
              "correct": false,
              "feedback": "Erreur : le second indice descend dans\nla ligne, il ne l'affiche pas en\nentier.\n"
            },
            {
              "text": "Il affiche `0`, l'élément d'indice $0$ dans la liste aplatie",
              "correct": false,
              "feedback": "Erreur : une liste de listes n'est pas\naplatie, les deux indices désignent la\nligne puis la colonne.\n"
            }
          ],
          "explanation": "Un tableau de données se range souvent\nainsi : une liste de lignes, chaque ligne\nétant une liste de valeurs. Le premier\nindice choisit la ligne, le second la\ncolonne."
        }
      ]
    },
    {
      "id": "q51",
      "difficulty": 3,
      "skills": [
        "liste",
        "copie",
        "tranche"
      ],
      "title": "Travailler sur une copie",
      "partie": "listes",
      "statement": "```python\noriginal = [1, 2, 3]\ncopie = original[:]\ncopie.append(4)\nprint(original, copie)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `[1, 2, 3] [1, 2, 3, 4]`",
          "correct": true,
          "feedback": "Bonne réponse : la tranche construit une\nnouvelle liste. Modifier la copie laisse\nl'originale intacte.\n"
        },
        {
          "text": "Il affiche `[1, 2, 3, 4] [1, 2, 3, 4]`",
          "correct": false,
          "feedback": "Erreur : ce serait le cas avec\n`copie = original`, qui donne deux noms\nà la même liste.\n"
        },
        {
          "text": "Il affiche `[1, 2, 3] [4]`",
          "correct": false,
          "feedback": "Erreur : la copie contient déjà les\ntrois valeurs avant l'ajout.\n"
        },
        {
          "text": "Il déclenche une exception, une liste ne se copiant pas ainsi",
          "correct": false,
          "feedback": "Erreur : `original[:]` est une écriture\ncourante et valide pour copier une\nliste.\n"
        }
      ],
      "explanation": "Retenir la différence : `copie = original`\npartage la même liste, `copie = original[:]`\nen fabrique une nouvelle. C'est la\nprécaution à prendre avant toute\nmodification."
    },
    {
      "id": "q52",
      "difficulty": 1,
      "skills": [
        "liste",
        "listes-imbriquees",
        "dimensions"
      ],
      "title": "Lignes et colonnes d'un tableau",
      "partie": "listes",
      "statement": "```python\nt = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]\n```\n\nCombien de lignes et de colonnes possède le tableau\n`t` ?",
      "options": [
        {
          "text": "$4$ lignes et $3$ colonnes",
          "correct": true,
          "feedback": "`len(t)` vaut $4$ (quatre listes intérieures) et\n`len(t[0])` vaut $3$.\n"
        },
        {
          "text": "$3$ lignes et $4$ colonnes",
          "correct": false,
          "feedback": "Chaque liste intérieure est une ligne ; il y en a\nquatre, de trois éléments chacune.\n"
        },
        {
          "text": "$12$ lignes et $1$ colonne",
          "correct": false,
          "feedback": "Les douze valeurs sont réparties en quatre listes\nde trois, pas en douze listes d'un élément.\n"
        },
        {
          "text": "$4$ lignes et $4$ colonnes",
          "correct": false,
          "feedback": "Une ligne compte trois éléments : `t[0]` est\n`[0, 1, 2]`.\n"
        }
      ],
      "explanation": "Un tableau à deux dimensions est une liste de\nlistes : la liste extérieure contient les lignes,\nchaque ligne contient les colonnes. `len(t)` donne\nle nombre de lignes, `len(t[0])` le nombre de\ncolonnes."
    },
    {
      "id": "q53",
      "difficulty": 2,
      "skills": [
        "liste",
        "listes-imbriquees",
        "modification"
      ],
      "title": "Modifier une case d'une grille",
      "partie": "listes",
      "statement": "```python\ngrille = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\ngrille[2][0] = 0\nprint(grille)\n```\n\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`[[1, 2, 3], [4, 5, 6], [0, 8, 9]]`",
          "correct": true,
          "feedback": "`grille[2]` est la troisième ligne, `[7, 8, 9]`,\net `[0]` en désigne le premier élément.\n"
        },
        {
          "text": "`[[0, 2, 3], [4, 5, 6], [7, 8, 9]]`",
          "correct": false,
          "feedback": "Ce serait `grille[0][0] = 0` ; le premier indice\nchoisit la ligne, pas la colonne.\n"
        },
        {
          "text": "`[[1, 2, 0], [4, 5, 6], [7, 8, 9]]`",
          "correct": false,
          "feedback": "Les indices sont lus dans l'ordre ligne puis\ncolonne : $2$ puis $0$, pas $0$ puis $2$.\n"
        },
        {
          "text": "`[[1, 2, 3], [4, 5, 6], [7, 8, 0]]`",
          "correct": false,
          "feedback": "`[0]` désigne le premier élément de la ligne, pas\nle dernier.\n"
        }
      ],
      "explanation": "`grille[i][j]` se lit de gauche à droite :\n`grille[i]` sélectionne la ligne d'indice `i`, puis\n`[j]` la colonne. Les indices commencent à $0$ :\nla ligne $2$ est la troisième, la colonne $0$ la\npremière."
    },
    {
      "id": "q54",
      "difficulty": 2,
      "skills": [
        "liste",
        "listes-imbriquees",
        "parcours",
        "accumulateur"
      ],
      "title": "Somme d'une colonne",
      "partie": "listes",
      "statement": "```python\nt = [[1, 2], [3, 4], [5, 6]]\ntotal = 0\nfor i in range(len(t)):\n    total = total + t[i][1]\nprint(total)\n```\n\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`12`",
          "correct": true,
          "feedback": "`t[i][1]` est le deuxième élément de chaque\nligne : $2 + 4 + 6 = 12$.\n"
        },
        {
          "text": "`9`",
          "correct": false,
          "feedback": "$1 + 3 + 5$ serait la somme de la colonne\nd'indice $0$.\n"
        },
        {
          "text": "`21`",
          "correct": false,
          "feedback": "C'est la somme de toutes les valeurs ; la boucle\nn'ajoute qu'un élément par ligne.\n"
        },
        {
          "text": "`6`",
          "correct": false,
          "feedback": "Ce serait la dernière valeur lue, pas la somme\naccumulée dans `total`.\n"
        }
      ],
      "explanation": "La boucle parcourt les indices de ligne ; à chaque\ntour, elle ajoute l'élément de colonne $1$ de la\nligne courante. Pour sommer une ligne entière, on\nparcourrait au contraire les indices de colonne à\nligne fixée.",
      "variants": [
        {
          "statement": "```python\nt = [[1, 2], [3, 4], [5, 6]]\ntotal = 0\nfor i in range(len(t)):\n    total = total + t[i][1]\nprint(total)\n```\n\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`12`",
              "correct": true,
              "feedback": "`t[i][1]` est le deuxième élément de chaque\nligne : $2 + 4 + 6 = 12$.\n"
            },
            {
              "text": "`9`",
              "correct": false,
              "feedback": "$1 + 3 + 5$ serait la somme de la colonne\nd'indice $0$.\n"
            },
            {
              "text": "`21`",
              "correct": false,
              "feedback": "C'est la somme de toutes les valeurs ; la boucle\nn'ajoute qu'un élément par ligne.\n"
            },
            {
              "text": "`6`",
              "correct": false,
              "feedback": "Ce serait la dernière valeur lue, pas la somme\naccumulée dans `total`.\n"
            }
          ],
          "explanation": "La boucle parcourt les indices de ligne ; à chaque\ntour, elle ajoute l'élément de colonne $1$ de la\nligne courante. Pour sommer une ligne entière, on\nparcourrait au contraire les indices de colonne à\nligne fixée."
        },
        {
          "statement": "```python\nt = [[2, 9], [4, 1], [7, 3], [5, 8]]\ntotal = 0\nfor i in range(len(t)):\n    total = total + t[i][0]\nprint(total)\n```\n\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`18`",
              "correct": true,
              "feedback": "`t[i][0]` est le premier élément de chaque\nligne : $2 + 4 + 7 + 5 = 18$.\n"
            },
            {
              "text": "`21`",
              "correct": false,
              "feedback": "$9 + 1 + 3 + 8$ serait la somme de la colonne\nd'indice $1$.\n"
            },
            {
              "text": "`39`",
              "correct": false,
              "feedback": "C'est la somme de toutes les valeurs ; la boucle\nn'ajoute qu'un élément par ligne.\n"
            },
            {
              "text": "`5`",
              "correct": false,
              "feedback": "Ce serait la dernière valeur lue, pas la somme\naccumulée dans `total`.\n"
            }
          ],
          "explanation": "La boucle parcourt les indices de ligne ; à chaque\ntour, elle ajoute l'élément de colonne $0$ de la\nligne courante. Pour sommer une ligne entière, on\nparcourrait au contraire les indices de colonne à\nligne fixée."
        },
        {
          "statement": "```python\nt = [[10, 1], [20, 2], [30, 3]]\ntotal = 0\nfor i in range(len(t)):\n    total = total + t[i][1]\nprint(total)\n```\n\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`6`",
              "correct": true,
              "feedback": "`t[i][1]` est le deuxième élément de chaque\nligne : $1 + 2 + 3 = 6$.\n"
            },
            {
              "text": "`60`",
              "correct": false,
              "feedback": "$10 + 20 + 30$ serait la somme de la colonne\nd'indice $0$.\n"
            },
            {
              "text": "`66`",
              "correct": false,
              "feedback": "C'est la somme de toutes les valeurs ; la boucle\nn'ajoute qu'un élément par ligne.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Ce serait la dernière valeur lue, pas la somme\naccumulée dans `total`.\n"
            }
          ],
          "explanation": "La boucle parcourt les indices de ligne ; à chaque\ntour, elle ajoute l'élément de colonne $1$ de la\nligne courante. Pour sommer une ligne entière, on\nparcourrait au contraire les indices de colonne à\nligne fixée."
        }
      ]
    },
    {
      "id": "q55",
      "difficulty": 2,
      "skills": [
        "liste",
        "comprehension-liste",
        "listes-imbriquees"
      ],
      "title": "Construire une grille par compréhension",
      "partie": "listes",
      "statement": "```python\nm = [[0 for j in range(3)] for i in range(2)]\n```\n\nQuelle valeur reçoit `m` ?",
      "options": [
        {
          "text": "`[[0, 0, 0], [0, 0, 0]]`",
          "correct": true,
          "feedback": "La compréhension extérieure fabrique deux lignes ;\nchacune est une liste de trois zéros.\n"
        },
        {
          "text": "`[[0, 0], [0, 0], [0, 0]]`",
          "correct": false,
          "feedback": "`range(2)` est à l'extérieur : il fixe le nombre\nde lignes à deux, et `range(3)` le nombre de\ncolonnes à trois.\n"
        },
        {
          "text": "`[0, 0, 0, 0, 0, 0]`",
          "correct": false,
          "feedback": "Les crochets intérieurs créent une liste par\nligne ; le résultat n'est pas aplati.\n"
        },
        {
          "text": "`[[0, 1, 2], [0, 1, 2]]`",
          "correct": false,
          "feedback": "L'expression produite est `0`, pas `j` ; la\nvariable `j` ne sert qu'à répéter trois fois.\n"
        }
      ],
      "explanation": "Une compréhension dans une compréhension : la partie\nintérieure `[0 for j in range(3)]` construit une\nligne, la partie extérieure répète cette\nconstruction pour chaque `i`. C'est la façon sûre de\ncréer une grille vide, chaque ligne étant une liste\ndistincte."
    },
    {
      "id": "q56",
      "difficulty": 3,
      "skills": [
        "liste",
        "listes-imbriquees",
        "alias",
        "reference-vs-copie"
      ],
      "title": "Deux lignes qui n'en font qu'une",
      "partie": "listes",
      "statement": "```python\nligne = [0, 0, 0]\nm = [ligne, ligne]\nm[0][0] = 5\nprint(m)\n```\n\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`[[5, 0, 0], [0, 0, 0]]`",
          "correct": false,
          "feedback": "Ce serait le cas si les deux lignes étaient des\nlistes distinctes ; ici elles sont la même liste.\n"
        },
        {
          "text": "`[[5, 0, 0], [5, 0, 0]]`",
          "correct": true,
          "feedback": "`m[0]` et `m[1]` désignent la même liste\n`ligne` ; la modifier par l'une se voit par\nl'autre.\n"
        },
        {
          "text": "`[[0, 0, 0], [0, 0, 0]]`",
          "correct": false,
          "feedback": "L'affectation `m[0][0] = 5` modifie bien la\nliste ; elle n'est pas sans effet.\n"
        },
        {
          "text": "Une erreur, car une liste ne peut pas contenir deux fois la même liste",
          "correct": false,
          "feedback": "Python l'accepte sans broncher ; c'est\nprécisément ce qui rend le piège discret.\n"
        }
      ],
      "explanation": "`[ligne, ligne]` ne copie pas `ligne` : il range\ndeux fois la même référence. Pour obtenir deux\nlignes indépendantes, on construit chaque ligne\nséparément, par exemple avec\n`[[0 for j in range(3)] for i in range(2)]`."
    },
    {
      "id": "q57",
      "difficulty": 2,
      "skills": [
        "comparaison-tuple-liste",
        "retour-multiple"
      ],
      "title": "Plusieurs valeurs renvoyées par une fonction",
      "partie": "choisir",
      "statement": "Une fonction `division(a, b)` doit renvoyer à la fois le quotient\net le reste de la division euclidienne de `a` par `b`. Quelle\nsolution est la plus naturelle en Python ?",
      "options": [
        {
          "text": "Renvoyer le tuple `(q, r)`, décomposé par `q, r = division(a, b)`",
          "correct": true,
          "feedback": "Bonne réponse : un tuple regroupe des valeurs de rôles fixés, en\nnombre connu. L'affectation multiple le décompose en une ligne,\nsans que personne n'ait à le modifier ensuite.\n"
        },
        {
          "text": "Écrire deux instructions `return` successives, une par valeur",
          "correct": false,
          "feedback": "Erreur : la première instruction `return` termine la fonction.\nLa seconde ne serait jamais exécutée et le reste serait perdu.\n"
        },
        {
          "text": "Renvoyer la chaîne `\"q,r\"` que l'appelant découpe et reconvertit",
          "correct": false,
          "feedback": "Erreur : transformer des entiers en texte oblige l'appelant à\ndécouper puis reconvertir. Un tuple transmet directement les deux\nentiers.\n"
        },
        {
          "text": "Ranger le reste dans une variable extérieure et ne renvoyer que le quotient",
          "correct": false,
          "feedback": "Erreur : une variable extérieure cache une partie du résultat et\nrend la fonction dépendante de son contexte. Renvoyer les deux\nvaleurs ensemble est plus clair et plus sûr.\n"
        }
      ],
      "explanation": "Dès qu'une fonction produit plusieurs résultats de nature fixée, le\ntuple est l'outil adapté : `return q, r` construit le tuple et\n`q, r = division(a, b)` le décompose."
    },
    {
      "id": "q58",
      "difficulty": 3,
      "skills": [
        "comparaison-tuple-liste",
        "immuabilite",
        "parametre"
      ],
      "title": "Données transmises sans risque de modification",
      "partie": "choisir",
      "statement": "Les réglages d'un jeu (largeur de la grille, hauteur, nombre de\nvies) sont transmis à de nombreuses fonctions, dont aucune ne doit\npouvoir les altérer par erreur. Quel choix de structure apporte\ncette garantie ?",
      "options": [
        {
          "text": "Un tuple, car toute tentative de modification en place lève une erreur",
          "correct": true,
          "feedback": "Bonne réponse : un tuple est immuable. Une fonction qui\ntenterait `reglages[0] = 50` provoquerait une erreur, ce qui\nrévèle le bug au lieu de le laisser corrompre les réglages.\n"
        },
        {
          "text": "Une liste, car une fonction ne peut jamais modifier une liste reçue en paramètre",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse. Une liste passée en paramètre est\npartagée avec l'appelant, et `reglages[0] = 50` dans la fonction\nmodifie bien la liste d'origine.\n"
        },
        {
          "text": "Une liste, accompagnée d'un commentaire demandant de ne pas la modifier",
          "correct": false,
          "feedback": "Erreur : un commentaire n'empêche rien. Une faute d'inattention\ndans l'une des fonctions suffirait à modifier les réglages sans\nqu'aucune erreur ne soit signalée.\n"
        },
        {
          "text": "Trois variables séparées, recopiées au début de chaque fonction",
          "correct": false,
          "feedback": "Erreur : recopier trois valeurs dans chaque fonction alourdit le\ncode et n'empêche pas une fonction d'oublier la copie. Le tuple\nregroupe les réglages et les protège d'un coup.\n"
        }
      ],
      "explanation": "Choisir un tuple pour des données qui ne doivent pas changer\ntransforme une erreur silencieuse en erreur signalée : c'est la\ngarantie que la liste ne peut pas offrir."
    },
    {
      "id": "q59",
      "difficulty": 1,
      "skills": [
        "tranche",
        "indices"
      ],
      "title": "Bornes d'une tranche",
      "partie": "slicing",
      "statement": "On dispose de `L = [10, 20, 30, 40, 50, 60]`.\nQue vaut `L[1:4]` ?",
      "options": [
        {
          "text": "La liste `[20, 30, 40]`",
          "correct": true,
          "feedback": "Bonne réponse : la tranche part de\nl'indice $1$ inclus et s'arrête juste\navant l'indice $4$.\n"
        },
        {
          "text": "La liste `[20, 30, 40, 50]`, les deux bornes étant incluses",
          "correct": false,
          "feedback": "Erreur : l'indice de fin est toujours\nexclu, comme dans `range(1, 4)`.\n"
        },
        {
          "text": "La liste `[30, 40, 50]`, les indices commençant à $1$",
          "correct": false,
          "feedback": "Erreur : le premier élément a l'indice\n$0$. L'élément d'indice $1$ est donc\n`20`.\n"
        },
        {
          "text": "La liste `[20, 50]`, formée des deux éléments aux bornes",
          "correct": false,
          "feedback": "Erreur : une tranche prend tous les\néléments entre les deux bornes, pas\nseulement les éléments aux bornes.\n"
        }
      ],
      "explanation": "`L[a:b]` contient les éléments d'indice\n$a$, $a + 1$, … jusqu'à $b - 1$ : on en\nobtient $b - a$. La fin est exclue, comme\npour `range(a, b)`.",
      "variants": [
        {
          "statement": "On dispose de `L = [10, 20, 30, 40, 50, 60]`.\nQue vaut `L[1:4]` ?",
          "options": [
            {
              "text": "La liste `[20, 30, 40]`",
              "correct": true,
              "feedback": "Bonne réponse : la tranche part de\nl'indice $1$ inclus et s'arrête juste\navant l'indice $4$.\n"
            },
            {
              "text": "La liste `[20, 30, 40, 50]`, les deux bornes étant incluses",
              "correct": false,
              "feedback": "Erreur : l'indice de fin est toujours\nexclu, comme dans `range(1, 4)`.\n"
            },
            {
              "text": "La liste `[30, 40, 50]`, les indices commençant à $1$",
              "correct": false,
              "feedback": "Erreur : le premier élément a l'indice\n$0$. L'élément d'indice $1$ est donc\n`20`.\n"
            },
            {
              "text": "La liste `[20, 50]`, formée des deux éléments aux bornes",
              "correct": false,
              "feedback": "Erreur : une tranche prend tous les\néléments entre les deux bornes, pas\nseulement les éléments aux bornes.\n"
            }
          ],
          "explanation": "`L[a:b]` contient les éléments d'indice\n$a$, $a + 1$, … jusqu'à $b - 1$ : on en\nobtient $b - a$. La fin est exclue, comme\npour `range(a, b)`."
        },
        {
          "statement": "On dispose de `L = [5, 8, 13, 21, 34, 55]`.\nQue vaut `L[2:5]` ?",
          "options": [
            {
              "text": "La liste `[13, 21, 34]`",
              "correct": true,
              "feedback": "Bonne réponse : la tranche part de\nl'indice $2$ inclus et s'arrête juste\navant l'indice $5$.\n"
            },
            {
              "text": "La liste `[13, 21, 34, 55]`, les deux bornes étant incluses",
              "correct": false,
              "feedback": "Erreur : l'indice de fin est toujours\nexclu, comme dans `range(2, 5)`.\n"
            },
            {
              "text": "La liste `[21, 34, 55]`, les indices commençant à $1$",
              "correct": false,
              "feedback": "Erreur : le premier élément a l'indice\n$0$. L'élément d'indice $2$ est donc\n`13`.\n"
            },
            {
              "text": "La liste `[13, 55]`, formée des deux éléments aux bornes",
              "correct": false,
              "feedback": "Erreur : une tranche prend tous les\néléments entre les deux bornes, pas\nseulement les éléments aux bornes.\n"
            }
          ],
          "explanation": "`L[a:b]` contient les éléments d'indice\n$a$, $a + 1$, … jusqu'à $b - 1$ : on en\nobtient $b - a$. La fin est exclue, comme\npour `range(a, b)`."
        },
        {
          "statement": "On dispose de `L = [7, 14, 21, 28, 35, 42]`.\nQue vaut `L[0:3]` ?",
          "options": [
            {
              "text": "La liste `[7, 14, 21]`",
              "correct": true,
              "feedback": "Bonne réponse : la tranche part de\nl'indice $0$ inclus et s'arrête juste\navant l'indice $3$.\n"
            },
            {
              "text": "La liste `[7, 14, 21, 28]`, les deux bornes étant incluses",
              "correct": false,
              "feedback": "Erreur : l'indice de fin est toujours\nexclu, comme dans `range(0, 3)`.\n"
            },
            {
              "text": "La liste `[14, 21, 28]`, les indices commençant à $1$",
              "correct": false,
              "feedback": "Erreur : le premier élément a l'indice\n$0$. L'élément d'indice $0$ est donc\n`7`.\n"
            },
            {
              "text": "La liste `[7, 28]`, formée des deux éléments aux bornes",
              "correct": false,
              "feedback": "Erreur : une tranche prend tous les\néléments entre les deux bornes, pas\nseulement les éléments aux bornes.\n"
            }
          ],
          "explanation": "`L[a:b]` contient les éléments d'indice\n$a$, $a + 1$, … jusqu'à $b - 1$ : on en\nobtient $b - a$. La fin est exclue, comme\npour `range(a, b)`."
        }
      ]
    },
    {
      "id": "q60",
      "difficulty": 1,
      "skills": [
        "tranche",
        "valeur-par-defaut"
      ],
      "title": "Début omis",
      "partie": "slicing",
      "statement": "On dispose de `L = [3, 1, 4, 1, 5, 9]`.\nQue vaut `L[:2]` ?",
      "options": [
        {
          "text": "La liste `[3, 1]`",
          "correct": true,
          "feedback": "Bonne réponse : le début omis vaut $0$.\nOn prend les éléments d'indice $0$ à\n$2$ exclu, soit les $2$ premiers.\n"
        },
        {
          "text": "La liste `[4, 1, 5, 9]`",
          "correct": false,
          "feedback": "Erreur : c'est `L[2:]`. Ici, le nombre\nest placé après les deux-points : c'est\nla fin, pas le début.\n"
        },
        {
          "text": "La liste `[3, 1, 4]`, l'indice $2$ étant inclus",
          "correct": false,
          "feedback": "Erreur : la fin est exclue, même quand\nle début est omis.\n"
        },
        {
          "text": "La liste `[5, 9]`, les $2$ derniers éléments",
          "correct": false,
          "feedback": "Erreur : les $2$ derniers s'écrivent\n`L[-2:]`. Sans signe moins, on part\ndu début.\n"
        }
      ],
      "explanation": "Un début omis vaut $0$ : `L[:k]` donne les\n$k$ premiers éléments.",
      "variants": [
        {
          "statement": "On dispose de `L = [3, 1, 4, 1, 5, 9]`.\nQue vaut `L[:2]` ?",
          "options": [
            {
              "text": "La liste `[3, 1]`",
              "correct": true,
              "feedback": "Bonne réponse : le début omis vaut $0$.\nOn prend les éléments d'indice $0$ à\n$2$ exclu, soit les $2$ premiers.\n"
            },
            {
              "text": "La liste `[4, 1, 5, 9]`",
              "correct": false,
              "feedback": "Erreur : c'est `L[2:]`. Ici, le nombre\nest placé après les deux-points : c'est\nla fin, pas le début.\n"
            },
            {
              "text": "La liste `[3, 1, 4]`, l'indice $2$ étant inclus",
              "correct": false,
              "feedback": "Erreur : la fin est exclue, même quand\nle début est omis.\n"
            },
            {
              "text": "La liste `[5, 9]`, les $2$ derniers éléments",
              "correct": false,
              "feedback": "Erreur : les $2$ derniers s'écrivent\n`L[-2:]`. Sans signe moins, on part\ndu début.\n"
            }
          ],
          "explanation": "Un début omis vaut $0$ : `L[:k]` donne les\n$k$ premiers éléments."
        },
        {
          "statement": "On dispose de `L = [8, 6, 7, 5, 3, 0, 9]`.\nQue vaut `L[:3]` ?",
          "options": [
            {
              "text": "La liste `[8, 6, 7]`",
              "correct": true,
              "feedback": "Bonne réponse : le début omis vaut $0$.\nOn prend les éléments d'indice $0$ à\n$3$ exclu, soit les $3$ premiers.\n"
            },
            {
              "text": "La liste `[5, 3, 0, 9]`",
              "correct": false,
              "feedback": "Erreur : c'est `L[3:]`. Ici, le nombre\nest placé après les deux-points : c'est\nla fin, pas le début.\n"
            },
            {
              "text": "La liste `[8, 6, 7, 5]`, l'indice $3$ étant inclus",
              "correct": false,
              "feedback": "Erreur : la fin est exclue, même quand\nle début est omis.\n"
            },
            {
              "text": "La liste `[3, 0, 9]`, les $3$ derniers éléments",
              "correct": false,
              "feedback": "Erreur : les $3$ derniers s'écrivent\n`L[-3:]`. Sans signe moins, on part\ndu début.\n"
            }
          ],
          "explanation": "Un début omis vaut $0$ : `L[:k]` donne les\n$k$ premiers éléments."
        },
        {
          "statement": "On dispose de `L = [2, 7, 1, 8, 2]`.\nQue vaut `L[:4]` ?",
          "options": [
            {
              "text": "La liste `[2, 7, 1, 8]`",
              "correct": true,
              "feedback": "Bonne réponse : le début omis vaut $0$.\nOn prend les éléments d'indice $0$ à\n$4$ exclu, soit les $4$ premiers.\n"
            },
            {
              "text": "La liste `[2]`",
              "correct": false,
              "feedback": "Erreur : c'est `L[4:]`. Ici, le nombre\nest placé après les deux-points : c'est\nla fin, pas le début.\n"
            },
            {
              "text": "La liste `[2, 7, 1, 8, 2]`, l'indice $4$ étant inclus",
              "correct": false,
              "feedback": "Erreur : la fin est exclue, même quand\nle début est omis.\n"
            },
            {
              "text": "La liste `[7, 1, 8, 2]`, les $4$ derniers éléments",
              "correct": false,
              "feedback": "Erreur : les $4$ derniers s'écrivent\n`L[-4:]`. Sans signe moins, on part\ndu début.\n"
            }
          ],
          "explanation": "Un début omis vaut $0$ : `L[:k]` donne les\n$k$ premiers éléments."
        }
      ]
    },
    {
      "id": "q61",
      "difficulty": 1,
      "skills": [
        "tranche",
        "valeur-par-defaut"
      ],
      "title": "Fin omise",
      "partie": "slicing",
      "statement": "On dispose de `L = [10, 20, 30, 40, 50]`.\nQue vaut `L[2:]` ?",
      "options": [
        {
          "text": "La liste `[30, 40, 50]`",
          "correct": true,
          "feedback": "Bonne réponse : la fin omise vaut\n`len(L)`. On prend tout à partir de\nl'indice $2$ inclus.\n"
        },
        {
          "text": "La liste `[10, 20]`",
          "correct": false,
          "feedback": "Erreur : c'est `L[:2]`, les éléments\nplacés avant l'indice $2$.\n"
        },
        {
          "text": "La liste `[40, 50]`, l'indice $2$ étant exclu",
          "correct": false,
          "feedback": "Erreur : seul l'indice de fin est\nexclu. Le début est toujours inclus.\n"
        },
        {
          "text": "La liste `[30]`, réduite à l'élément d'indice $2$",
          "correct": false,
          "feedback": "Erreur : les deux-points demandent une\ntranche, qui va ici jusqu'au bout de la\nliste.\n"
        }
      ],
      "explanation": "Une fin omise vaut `len(L)` : `L[k:]`\ndonne tout à partir de l'indice $k$\ninclus. Avec `L[:k]`, les deux tranches\nse partagent la liste sans chevauchement.",
      "variants": [
        {
          "statement": "On dispose de `L = [10, 20, 30, 40, 50]`.\nQue vaut `L[2:]` ?",
          "options": [
            {
              "text": "La liste `[30, 40, 50]`",
              "correct": true,
              "feedback": "Bonne réponse : la fin omise vaut\n`len(L)`. On prend tout à partir de\nl'indice $2$ inclus.\n"
            },
            {
              "text": "La liste `[10, 20]`",
              "correct": false,
              "feedback": "Erreur : c'est `L[:2]`, les éléments\nplacés avant l'indice $2$.\n"
            },
            {
              "text": "La liste `[40, 50]`, l'indice $2$ étant exclu",
              "correct": false,
              "feedback": "Erreur : seul l'indice de fin est\nexclu. Le début est toujours inclus.\n"
            },
            {
              "text": "La liste `[30]`, réduite à l'élément d'indice $2$",
              "correct": false,
              "feedback": "Erreur : les deux-points demandent une\ntranche, qui va ici jusqu'au bout de la\nliste.\n"
            }
          ],
          "explanation": "Une fin omise vaut `len(L)` : `L[k:]`\ndonne tout à partir de l'indice $k$\ninclus. Avec `L[:k]`, les deux tranches\nse partagent la liste sans chevauchement."
        },
        {
          "statement": "On dispose de `L = [4, 9, 16, 25, 36, 49]`.\nQue vaut `L[3:]` ?",
          "options": [
            {
              "text": "La liste `[25, 36, 49]`",
              "correct": true,
              "feedback": "Bonne réponse : la fin omise vaut\n`len(L)`. On prend tout à partir de\nl'indice $3$ inclus.\n"
            },
            {
              "text": "La liste `[4, 9, 16]`",
              "correct": false,
              "feedback": "Erreur : c'est `L[:3]`, les éléments\nplacés avant l'indice $3$.\n"
            },
            {
              "text": "La liste `[36, 49]`, l'indice $3$ étant exclu",
              "correct": false,
              "feedback": "Erreur : seul l'indice de fin est\nexclu. Le début est toujours inclus.\n"
            },
            {
              "text": "La liste `[25]`, réduite à l'élément d'indice $3$",
              "correct": false,
              "feedback": "Erreur : les deux-points demandent une\ntranche, qui va ici jusqu'au bout de la\nliste.\n"
            }
          ],
          "explanation": "Une fin omise vaut `len(L)` : `L[k:]`\ndonne tout à partir de l'indice $k$\ninclus. Avec `L[:k]`, les deux tranches\nse partagent la liste sans chevauchement."
        },
        {
          "statement": "On dispose de `L = [1, 3, 5, 7, 9, 11, 13]`.\nQue vaut `L[5:]` ?",
          "options": [
            {
              "text": "La liste `[11, 13]`",
              "correct": true,
              "feedback": "Bonne réponse : la fin omise vaut\n`len(L)`. On prend tout à partir de\nl'indice $5$ inclus.\n"
            },
            {
              "text": "La liste `[1, 3, 5, 7, 9]`",
              "correct": false,
              "feedback": "Erreur : c'est `L[:5]`, les éléments\nplacés avant l'indice $5$.\n"
            },
            {
              "text": "La liste `[13]`, l'indice $5$ étant exclu",
              "correct": false,
              "feedback": "Erreur : seul l'indice de fin est\nexclu. Le début est toujours inclus.\n"
            },
            {
              "text": "La liste `[11]`, réduite à l'élément d'indice $5$",
              "correct": false,
              "feedback": "Erreur : les deux-points demandent une\ntranche, qui va ici jusqu'au bout de la\nliste.\n"
            }
          ],
          "explanation": "Une fin omise vaut `len(L)` : `L[k:]`\ndonne tout à partir de l'indice $k$\ninclus. Avec `L[:k]`, les deux tranches\nse partagent la liste sans chevauchement."
        }
      ]
    },
    {
      "id": "q62",
      "difficulty": 2,
      "skills": [
        "tranche",
        "indices-negatifs"
      ],
      "title": "Les derniers éléments",
      "partie": "slicing",
      "statement": "On dispose de `L = [2, 4, 6, 8, 10]`.\nQue vaut `L[-2:]` ?",
      "options": [
        {
          "text": "La liste `[8, 10]`, les $2$ derniers éléments",
          "correct": true,
          "feedback": "Bonne réponse : l'indice $-2$ désigne\nle $2$e élément en partant de la fin,\net la fin omise va jusqu'au bout.\n"
        },
        {
          "text": "La liste `[2, 4]`, les $2$ premiers éléments",
          "correct": false,
          "feedback": "Erreur : un indice négatif se compte à\npartir de la fin de la liste.\n"
        },
        {
          "text": "La liste `[2, 4, 6]`, tout sauf les $2$ derniers",
          "correct": false,
          "feedback": "Erreur : c'est `L[:-2]`, où $-2$ est\nla fin de la tranche et non son début.\n"
        },
        {
          "text": "La liste `[10, 8]`, les $2$ derniers dans l'ordre inverse",
          "correct": false,
          "feedback": "Erreur : sans pas négatif, une tranche\ngarde toujours l'ordre de la liste.\n"
        }
      ],
      "explanation": "`L[-k:]` donne les $k$ derniers éléments,\n`L[:-k]` tout sauf ces $k$ derniers.",
      "variants": [
        {
          "statement": "On dispose de `L = [2, 4, 6, 8, 10]`.\nQue vaut `L[-2:]` ?",
          "options": [
            {
              "text": "La liste `[8, 10]`, les $2$ derniers éléments",
              "correct": true,
              "feedback": "Bonne réponse : l'indice $-2$ désigne\nle $2$e élément en partant de la fin,\net la fin omise va jusqu'au bout.\n"
            },
            {
              "text": "La liste `[2, 4]`, les $2$ premiers éléments",
              "correct": false,
              "feedback": "Erreur : un indice négatif se compte à\npartir de la fin de la liste.\n"
            },
            {
              "text": "La liste `[2, 4, 6]`, tout sauf les $2$ derniers",
              "correct": false,
              "feedback": "Erreur : c'est `L[:-2]`, où $-2$ est\nla fin de la tranche et non son début.\n"
            },
            {
              "text": "La liste `[10, 8]`, les $2$ derniers dans l'ordre inverse",
              "correct": false,
              "feedback": "Erreur : sans pas négatif, une tranche\ngarde toujours l'ordre de la liste.\n"
            }
          ],
          "explanation": "`L[-k:]` donne les $k$ derniers éléments,\n`L[:-k]` tout sauf ces $k$ derniers."
        },
        {
          "statement": "On dispose de `L = [11, 22, 33, 44, 55, 66, 77]`.\nQue vaut `L[-3:]` ?",
          "options": [
            {
              "text": "La liste `[55, 66, 77]`, les $3$ derniers éléments",
              "correct": true,
              "feedback": "Bonne réponse : l'indice $-3$ désigne\nle $3$e élément en partant de la fin,\net la fin omise va jusqu'au bout.\n"
            },
            {
              "text": "La liste `[11, 22, 33]`, les $3$ premiers éléments",
              "correct": false,
              "feedback": "Erreur : un indice négatif se compte à\npartir de la fin de la liste.\n"
            },
            {
              "text": "La liste `[11, 22, 33, 44]`, tout sauf les $3$ derniers",
              "correct": false,
              "feedback": "Erreur : c'est `L[:-3]`, où $-3$ est\nla fin de la tranche et non son début.\n"
            },
            {
              "text": "La liste `[77, 66, 55]`, les $3$ derniers dans l'ordre inverse",
              "correct": false,
              "feedback": "Erreur : sans pas négatif, une tranche\ngarde toujours l'ordre de la liste.\n"
            }
          ],
          "explanation": "`L[-k:]` donne les $k$ derniers éléments,\n`L[:-k]` tout sauf ces $k$ derniers."
        },
        {
          "statement": "On dispose de `L = [9, 8, 7, 6, 5, 4, 3]`.\nQue vaut `L[-4:]` ?",
          "options": [
            {
              "text": "La liste `[6, 5, 4, 3]`, les $4$ derniers éléments",
              "correct": true,
              "feedback": "Bonne réponse : l'indice $-4$ désigne\nle $4$e élément en partant de la fin,\net la fin omise va jusqu'au bout.\n"
            },
            {
              "text": "La liste `[9, 8, 7, 6]`, les $4$ premiers éléments",
              "correct": false,
              "feedback": "Erreur : un indice négatif se compte à\npartir de la fin de la liste.\n"
            },
            {
              "text": "La liste `[9, 8, 7]`, tout sauf les $4$ derniers",
              "correct": false,
              "feedback": "Erreur : c'est `L[:-4]`, où $-4$ est\nla fin de la tranche et non son début.\n"
            },
            {
              "text": "La liste `[3, 4, 5, 6]`, les $4$ derniers dans l'ordre inverse",
              "correct": false,
              "feedback": "Erreur : sans pas négatif, une tranche\ngarde toujours l'ordre de la liste.\n"
            }
          ],
          "explanation": "`L[-k:]` donne les $k$ derniers éléments,\n`L[:-k]` tout sauf ces $k$ derniers."
        }
      ]
    },
    {
      "id": "q63",
      "difficulty": 2,
      "skills": [
        "tranche",
        "indices-negatifs",
        "chaine"
      ],
      "title": "Retirer le premier et le dernier caractère",
      "partie": "slicing",
      "statement": "On dispose de `s = \"bonjour\"`.\nQue vaut `s[1:-1]` ?",
      "options": [
        {
          "text": "La chaîne `'onjou'`",
          "correct": true,
          "feedback": "Bonne réponse : on part de l'indice $1$\n(le `'o'`) et on s'arrête avant le\ndernier caractère, d'indice $-1$.\n"
        },
        {
          "text": "La chaîne `'bonjou'`",
          "correct": false,
          "feedback": "Erreur : l'indice $1$ désigne le\ndeuxième caractère. Le `'b'` a l'indice\n$0$ et n'est pas pris.\n"
        },
        {
          "text": "La chaîne `'onjour'`",
          "correct": false,
          "feedback": "Erreur : la fin $-1$ est exclue, le\ndernier caractère n'est donc pas pris.\n"
        },
        {
          "text": "Une erreur, les indices négatifs étant interdits dans une tranche",
          "correct": false,
          "feedback": "Erreur : les indices négatifs sont\npermis dans une tranche, comme dans\nun accès simple.\n"
        }
      ],
      "explanation": "`s[1:-1]` retire le premier et le dernier\ncaractère. C'est le même découpage sur une\nchaîne que sur une liste."
    },
    {
      "id": "q64",
      "difficulty": 2,
      "skills": [
        "tranche",
        "pas"
      ],
      "title": "Un élément sur deux",
      "partie": "slicing",
      "statement": "On dispose de `L = [10, 11, 12, 13, 14, 15, 16]`.\nQue vaut `L[1::2]` ?",
      "options": [
        {
          "text": "La liste `[11, 13, 15]`",
          "correct": true,
          "feedback": "Bonne réponse : on part de l'indice\n$1$ et on avance de $2$ en $2$\njusqu'au bout de la liste.\n"
        },
        {
          "text": "La liste `[10, 12, 14, 16]`",
          "correct": false,
          "feedback": "Erreur : c'est un élément sur deux,\nmais à partir de l'autre indice de\ndépart. Ici, le départ est $1$.\n"
        },
        {
          "text": "La liste `[11]`, le $2$ étant la fin",
          "correct": false,
          "feedback": "Erreur : avec deux fois deux-points, le\nnombre placé en dernier est le pas,\npas la fin.\n"
        },
        {
          "text": "La liste `[13, 14, 15, 16]`, le $2$ s'ajoutant au début",
          "correct": false,
          "feedback": "Erreur : le troisième nombre est\nl'écart entre deux indices pris, pas un\ndécalage du début.\n"
        }
      ],
      "explanation": "`L[début:fin:pas]` : le pas est l'écart\nentre deux indices pris. `L[::2]` prend\nles indices pairs, `L[1::2]` les indices\nimpairs.",
      "variants": [
        {
          "statement": "On dispose de `L = [10, 11, 12, 13, 14, 15, 16]`.\nQue vaut `L[1::2]` ?",
          "options": [
            {
              "text": "La liste `[11, 13, 15]`",
              "correct": true,
              "feedback": "Bonne réponse : on part de l'indice\n$1$ et on avance de $2$ en $2$\njusqu'au bout de la liste.\n"
            },
            {
              "text": "La liste `[10, 12, 14, 16]`",
              "correct": false,
              "feedback": "Erreur : c'est un élément sur deux,\nmais à partir de l'autre indice de\ndépart. Ici, le départ est $1$.\n"
            },
            {
              "text": "La liste `[11]`, le $2$ étant la fin",
              "correct": false,
              "feedback": "Erreur : avec deux fois deux-points, le\nnombre placé en dernier est le pas,\npas la fin.\n"
            },
            {
              "text": "La liste `[13, 14, 15, 16]`, le $2$ s'ajoutant au début",
              "correct": false,
              "feedback": "Erreur : le troisième nombre est\nl'écart entre deux indices pris, pas un\ndécalage du début.\n"
            }
          ],
          "explanation": "`L[début:fin:pas]` : le pas est l'écart\nentre deux indices pris. `L[::2]` prend\nles indices pairs, `L[1::2]` les indices\nimpairs."
        },
        {
          "statement": "On dispose de `L = [3, 6, 9, 12, 15, 18]`.\nQue vaut `L[0::2]` ?",
          "options": [
            {
              "text": "La liste `[3, 9, 15]`",
              "correct": true,
              "feedback": "Bonne réponse : on part de l'indice\n$0$ et on avance de $2$ en $2$\njusqu'au bout de la liste.\n"
            },
            {
              "text": "La liste `[6, 12, 18]`",
              "correct": false,
              "feedback": "Erreur : c'est un élément sur deux,\nmais à partir de l'autre indice de\ndépart. Ici, le départ est $0$.\n"
            },
            {
              "text": "La liste `[3, 6]`, le $2$ étant la fin",
              "correct": false,
              "feedback": "Erreur : avec deux fois deux-points, le\nnombre placé en dernier est le pas,\npas la fin.\n"
            },
            {
              "text": "La liste `[9, 12, 15, 18]`, le $2$ s'ajoutant au début",
              "correct": false,
              "feedback": "Erreur : le troisième nombre est\nl'écart entre deux indices pris, pas un\ndécalage du début.\n"
            }
          ],
          "explanation": "`L[début:fin:pas]` : le pas est l'écart\nentre deux indices pris. `L[::2]` prend\nles indices pairs, `L[1::2]` les indices\nimpairs."
        },
        {
          "statement": "On dispose de `L = [2, 4, 6, 8, 10, 12, 14, 16]`.\nQue vaut `L[1::2]` ?",
          "options": [
            {
              "text": "La liste `[4, 8, 12, 16]`",
              "correct": true,
              "feedback": "Bonne réponse : on part de l'indice\n$1$ et on avance de $2$ en $2$\njusqu'au bout de la liste.\n"
            },
            {
              "text": "La liste `[2, 6, 10, 14]`",
              "correct": false,
              "feedback": "Erreur : c'est un élément sur deux,\nmais à partir de l'autre indice de\ndépart. Ici, le départ est $1$.\n"
            },
            {
              "text": "La liste `[4]`, le $2$ étant la fin",
              "correct": false,
              "feedback": "Erreur : avec deux fois deux-points, le\nnombre placé en dernier est le pas,\npas la fin.\n"
            },
            {
              "text": "La liste `[8, 10, 12, 14, 16]`, le $2$ s'ajoutant au début",
              "correct": false,
              "feedback": "Erreur : le troisième nombre est\nl'écart entre deux indices pris, pas un\ndécalage du début.\n"
            }
          ],
          "explanation": "`L[début:fin:pas]` : le pas est l'écart\nentre deux indices pris. `L[::2]` prend\nles indices pairs, `L[1::2]` les indices\nimpairs."
        }
      ]
    },
    {
      "id": "q65",
      "difficulty": 2,
      "skills": [
        "tranche",
        "pas-negatif",
        "chaine"
      ],
      "title": "Inverser une chaîne",
      "partie": "slicing",
      "statement": "On dispose de `mot = \"python\"`.\nQue vaut `mot[::-1]` ?",
      "options": [
        {
          "text": "La chaîne `'nohtyp'`",
          "correct": true,
          "feedback": "Bonne réponse : avec un pas de $-1$,\non parcourt la chaîne de la fin vers le\ndébut.\n"
        },
        {
          "text": "La chaîne `'pytho'`, sans le dernier caractère",
          "correct": false,
          "feedback": "Erreur : c'est `mot[:-1]`. Ici, $-1$\nest en troisième position : c'est le\npas.\n"
        },
        {
          "text": "La chaîne `'n'`, le dernier caractère",
          "correct": false,
          "feedback": "Erreur : c'est `mot[-1]`, un accès à un\nseul caractère, sans deux-points.\n"
        },
        {
          "text": "La chaîne `'ython'`, sans le premier caractère",
          "correct": false,
          "feedback": "Erreur : c'est `mot[1:]`. Un pas\nnégatif ne retire rien, il renverse\nl'ordre.\n"
        }
      ],
      "explanation": "`mot[::-1]` renvoie la séquence à\nl'envers. Cela marche aussi sur une liste\nou un tuple.",
      "variants": [
        {
          "statement": "On dispose de `mot = \"python\"`.\nQue vaut `mot[::-1]` ?",
          "options": [
            {
              "text": "La chaîne `'nohtyp'`",
              "correct": true,
              "feedback": "Bonne réponse : avec un pas de $-1$,\non parcourt la chaîne de la fin vers le\ndébut.\n"
            },
            {
              "text": "La chaîne `'pytho'`, sans le dernier caractère",
              "correct": false,
              "feedback": "Erreur : c'est `mot[:-1]`. Ici, $-1$\nest en troisième position : c'est le\npas.\n"
            },
            {
              "text": "La chaîne `'n'`, le dernier caractère",
              "correct": false,
              "feedback": "Erreur : c'est `mot[-1]`, un accès à un\nseul caractère, sans deux-points.\n"
            },
            {
              "text": "La chaîne `'ython'`, sans le premier caractère",
              "correct": false,
              "feedback": "Erreur : c'est `mot[1:]`. Un pas\nnégatif ne retire rien, il renverse\nl'ordre.\n"
            }
          ],
          "explanation": "`mot[::-1]` renvoie la séquence à\nl'envers. Cela marche aussi sur une liste\nou un tuple."
        },
        {
          "statement": "On dispose de `mot = \"tableau\"`.\nQue vaut `mot[::-1]` ?",
          "options": [
            {
              "text": "La chaîne `'uaelbat'`",
              "correct": true,
              "feedback": "Bonne réponse : avec un pas de $-1$,\non parcourt la chaîne de la fin vers le\ndébut.\n"
            },
            {
              "text": "La chaîne `'tablea'`, sans le dernier caractère",
              "correct": false,
              "feedback": "Erreur : c'est `mot[:-1]`. Ici, $-1$\nest en troisième position : c'est le\npas.\n"
            },
            {
              "text": "La chaîne `'u'`, le dernier caractère",
              "correct": false,
              "feedback": "Erreur : c'est `mot[-1]`, un accès à un\nseul caractère, sans deux-points.\n"
            },
            {
              "text": "La chaîne `'ableau'`, sans le premier caractère",
              "correct": false,
              "feedback": "Erreur : c'est `mot[1:]`. Un pas\nnégatif ne retire rien, il renverse\nl'ordre.\n"
            }
          ],
          "explanation": "`mot[::-1]` renvoie la séquence à\nl'envers. Cela marche aussi sur une liste\nou un tuple."
        },
        {
          "statement": "On dispose de `mot = \"boucle\"`.\nQue vaut `mot[::-1]` ?",
          "options": [
            {
              "text": "La chaîne `'elcuob'`",
              "correct": true,
              "feedback": "Bonne réponse : avec un pas de $-1$,\non parcourt la chaîne de la fin vers le\ndébut.\n"
            },
            {
              "text": "La chaîne `'boucl'`, sans le dernier caractère",
              "correct": false,
              "feedback": "Erreur : c'est `mot[:-1]`. Ici, $-1$\nest en troisième position : c'est le\npas.\n"
            },
            {
              "text": "La chaîne `'e'`, le dernier caractère",
              "correct": false,
              "feedback": "Erreur : c'est `mot[-1]`, un accès à un\nseul caractère, sans deux-points.\n"
            },
            {
              "text": "La chaîne `'oucle'`, sans le premier caractère",
              "correct": false,
              "feedback": "Erreur : c'est `mot[1:]`. Un pas\nnégatif ne retire rien, il renverse\nl'ordre.\n"
            }
          ],
          "explanation": "`mot[::-1]` renvoie la séquence à\nl'envers. Cela marche aussi sur une liste\nou un tuple."
        }
      ]
    },
    {
      "id": "q66",
      "difficulty": 2,
      "skills": [
        "tranche",
        "pas-negatif",
        "fonction"
      ],
      "title": "Tester un palindrome",
      "partie": "slicing",
      "statement": "```python\ndef est_palindrome(mot):\n    return mot == mot[::-1]\n\nprint(est_palindrome(\"kayak\"))\nprint(est_palindrome(\"radis\"))\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `True` puis `False`",
          "correct": true,
          "feedback": "Bonne réponse : `\"kayak\"` se lit de la\nmême façon dans les deux sens, pas\n`\"radis\"`, qui donne `\"sidar\"`.\n"
        },
        {
          "text": "Il affiche `True` puis `True`",
          "correct": false,
          "feedback": "Erreur : `\"radis\"[::-1]` vaut `\"sidar\"`,\nqui est différent de `\"radis\"`.\n"
        },
        {
          "text": "Il affiche `False` puis `False`, `mot[::-1]` ne changeant rien",
          "correct": false,
          "feedback": "Erreur : avec un pas de $-1$, la chaîne\nest bien renversée.\n"
        },
        {
          "text": "Une erreur, une chaîne ne pouvant pas être comparée à une tranche",
          "correct": false,
          "feedback": "Erreur : une tranche de chaîne est une\nchaîne, la comparaison est donc\npossible.\n"
        }
      ],
      "explanation": "Un mot est un palindrome quand il est égal\nà son inverse : `mot == mot[::-1]` le teste\nen une seule ligne."
    },
    {
      "id": "q67",
      "difficulty": 2,
      "skills": [
        "tranche",
        "acces-indice",
        "type"
      ],
      "title": "Indexation ou tranche",
      "partie": "slicing",
      "statement": "```python\nL = ['a', 'b', 'c', 'd']\nx = L[1]\ny = L[1:2]\n```\nQue valent `x` et `y` ?",
      "options": [
        {
          "text": "`x` vaut `'b'` et `y` vaut `['b']`",
          "correct": true,
          "feedback": "Bonne réponse : `L[1]` est un élément,\n`L[1:2]` est une liste qui contient cet\nélément.\n"
        },
        {
          "text": "`x` et `y` valent tous deux `'b'`",
          "correct": false,
          "feedback": "Erreur : avec deux-points, le résultat\nest toujours une liste, même s'il n'a\nqu'un élément.\n"
        },
        {
          "text": "`x` et `y` valent tous deux `['b']`",
          "correct": false,
          "feedback": "Erreur : sans deux-points, on accède à\nl'élément lui-même, ici une chaîne.\n"
        },
        {
          "text": "`x` vaut `'b'` et `y` vaut `['b', 'c']`",
          "correct": false,
          "feedback": "Erreur : l'indice de fin $2$ est exclu,\nseul l'élément d'indice $1$ est pris.\n"
        }
      ],
      "explanation": "L'indexation renvoie un élément, la\ntranche renvoie une nouvelle séquence. On\nne peut donc pas écrire `L[1:2] + 1` si\n`L` contient des nombres."
    },
    {
      "id": "q68",
      "difficulty": 2,
      "skills": [
        "tranche",
        "bornes"
      ],
      "title": "Tranche qui dépasse la liste",
      "partie": "slicing",
      "statement": "On dispose de `L = [1, 2, 3, 4]`.\nQue vaut `L[2:10]` ?",
      "options": [
        {
          "text": "La liste `[3, 4]`",
          "correct": true,
          "feedback": "Bonne réponse : une borne trop grande\nest ramenée à la longueur de la liste,\nsans erreur.\n"
        },
        {
          "text": "Une erreur `IndexError`, l'indice $10$ n'existant pas",
          "correct": false,
          "feedback": "Erreur : seul l'accès à un élément,\ncomme `L[10]`, provoque `IndexError`.\nUne tranche s'arrête au bout de la liste.\n"
        },
        {
          "text": "La liste `[3, 4, None, None, None, None, None, None]`",
          "correct": false,
          "feedback": "Erreur : une tranche ne crée pas\nd'éléments, elle prend seulement ceux\nqui existent.\n"
        },
        {
          "text": "La liste vide `[]`",
          "correct": false,
          "feedback": "Erreur : les éléments d'indice $2$ et\n$3$ existent et sont pris.\n"
        }
      ],
      "explanation": "Contrairement à `L[i]`, une tranche ne\ndéclenche jamais d'erreur d'indice : les\nbornes trop grandes sont ramenées à la\nlongueur."
    },
    {
      "id": "q69",
      "difficulty": 3,
      "skills": [
        "tranche",
        "bornes"
      ],
      "title": "Tranche vide",
      "partie": "slicing",
      "statement": "On dispose de `L = [5, 6, 7, 8, 9]`.\nQue vaut `L[3:1]` ?",
      "options": [
        {
          "text": "La liste vide `[]`",
          "correct": true,
          "feedback": "Bonne réponse : avec le pas $1$ par\ndéfaut, on avance vers la droite. Partir\nde l'indice $3$ pour s'arrêter avant\nl'indice $1$ ne prend aucun élément.\n"
        },
        {
          "text": "La liste `[8, 7]`, parcourue à l'envers",
          "correct": false,
          "feedback": "Erreur : il faudrait un pas négatif,\n`L[3:1:-1]`, pour aller vers la gauche.\n"
        },
        {
          "text": "La liste `[6, 7]`, les bornes étant remises dans l'ordre",
          "correct": false,
          "feedback": "Erreur : Python ne réordonne pas les\nbornes. `[6, 7]` est `L[1:3]`.\n"
        },
        {
          "text": "Une erreur, le début étant plus grand que la fin",
          "correct": false,
          "feedback": "Erreur : une tranche ne déclenche pas\nd'erreur ; elle est simplement vide.\n"
        }
      ],
      "explanation": "Avec un pas positif, si le début n'est pas\navant la fin, la tranche est vide."
    },
    {
      "id": "q70",
      "difficulty": 2,
      "skills": [
        "tranche",
        "copie",
        "reference-vs-copie"
      ],
      "title": "Modifier une tranche",
      "partie": "slicing",
      "statement": "```python\nL = [1, 2, 3, 4]\nT = L[1:3]\nT[0] = 99\nprint(L)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `[1, 2, 3, 4]`",
          "correct": true,
          "feedback": "Bonne réponse : `T` est une nouvelle\nliste, `[2, 3]`. La modifier ne change\npas `L`.\n"
        },
        {
          "text": "Il affiche `[1, 99, 3, 4]`",
          "correct": false,
          "feedback": "Erreur : ce serait le cas si `T` était\nun autre nom de la même liste. Une\ntranche est une copie.\n"
        },
        {
          "text": "Il affiche `[99, 2, 3, 4]`",
          "correct": false,
          "feedback": "Erreur : `T[0]` est le premier élément\nde `T`, et `T` est de toute façon\ndistincte de `L`.\n"
        },
        {
          "text": "Une erreur, une tranche ne pouvant pas être modifiée",
          "correct": false,
          "feedback": "Erreur : `T` est une liste ordinaire, on\npeut la modifier.\n"
        }
      ],
      "explanation": "Une tranche fabrique une nouvelle\nséquence. C'est pourquoi `L[:]` sert à\ncopier une liste."
    },
    {
      "id": "q71",
      "difficulty": 3,
      "skills": [
        "tranche",
        "modification",
        "mutabilite"
      ],
      "title": "Remplacer une portion de liste",
      "partie": "slicing",
      "statement": "```python\nL = ['a', 'b', 'c', 'd', 'e']\nL[1:3] = ['X', 'Y', 'Z']\nprint(L)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Il affiche `['a', 'X', 'Y', 'Z', 'd', 'e']`",
          "correct": true,
          "feedback": "Bonne réponse : les éléments d'indice\n$1$ et $2$ (`'b'` et `'c'`) sont remplacés\npar les trois nouveaux. La liste\ns'allonge d'un élément.\n"
        },
        {
          "text": "Il affiche `['a', 'X', 'Y', 'd', 'e']`, la liste gardant sa taille",
          "correct": false,
          "feedback": "Erreur : tous les éléments de droite\nsont insérés, même s'ils sont plus\nnombreux que ceux qu'ils remplacent.\n"
        },
        {
          "text": "Il affiche `['a', 'X', 'Y', 'Z', 'e']`",
          "correct": false,
          "feedback": "Erreur : la fin $3$ est exclue, `'d'`\nn'est pas remplacé.\n"
        },
        {
          "text": "Une erreur, les deux tailles étant différentes",
          "correct": false,
          "feedback": "Erreur : avec un pas de $1$, Python\naccepte un remplacement de taille\ndifférente.\n"
        }
      ],
      "explanation": "Sur une liste, on peut affecter une\ntranche : la portion choisie est remplacée\npar une autre, de taille quelconque."
    },
    {
      "id": "q72",
      "difficulty": 2,
      "skills": [
        "tranche",
        "chaine",
        "immuabilite"
      ],
      "title": "Modifier une tranche de chaîne",
      "partie": "slicing",
      "statement": "```python\ns = \"python\"\ns[0:2] = \"PY\"\n```\nQue se passe-t-il ?",
      "options": [
        {
          "text": "Une erreur `TypeError` : une chaîne ne se modifie pas",
          "correct": true,
          "feedback": "Bonne réponse : une chaîne est\nimmuable. On construit une nouvelle\nchaîne : `s = \"PY\" + s[2:]`.\n"
        },
        {
          "text": "`s` devient `'PYthon'`",
          "correct": false,
          "feedback": "Erreur : l'affectation d'une tranche\nn'est permise que sur une liste.\n"
        },
        {
          "text": "`s` devient `'PYpython'`, `\"PY\"` étant inséré au début",
          "correct": false,
          "feedback": "Erreur : même sur une liste, la portion\nserait remplacée et non complétée. Et\nsur une chaîne, Python refuse.\n"
        },
        {
          "text": "`s` reste `'python'`, sans message d'erreur",
          "correct": false,
          "feedback": "Erreur : Python signale l'opération\ninterdite par une erreur.\n"
        }
      ],
      "explanation": "On découpe une chaîne librement, mais on\nne la modifie pas : pour changer des\ncaractères, on fabrique une nouvelle\nchaîne avec des tranches et `+`."
    },
    {
      "id": "q73",
      "difficulty": 3,
      "skills": [
        "tranche",
        "concatenation"
      ],
      "title": "Rotation d'une liste",
      "partie": "slicing",
      "statement": "On dispose de `L = [1, 2, 3, 4, 5]`.\nQue vaut `L[2:] + L[:2]` ?",
      "options": [
        {
          "text": "La liste `[3, 4, 5, 1, 2]`",
          "correct": true,
          "feedback": "Bonne réponse : on met la fin de la\nliste (à partir de l'indice $2$)\ndevant ses $2$ premiers éléments.\n"
        },
        {
          "text": "La liste `[4, 5, 1, 2, 3]`",
          "correct": false,
          "feedback": "Erreur : c'est la rotation dans l'autre\nsens, `L[-2:] + L[:-2]`.\n"
        },
        {
          "text": "La liste `[1, 2, 3, 4, 5]`, inchangée",
          "correct": false,
          "feedback": "Erreur : l'ordre des deux morceaux\ncompte. Ici, la fin passe devant.\n"
        },
        {
          "text": "La liste `[3, 4, 5]`",
          "correct": false,
          "feedback": "Erreur : `+` met bout à bout les deux\ntranches, aucun élément n'est perdu.\n"
        }
      ],
      "explanation": "`L[k:] + L[:k]` fait tourner la liste de\n$k$ crans vers la gauche : les $k$ premiers\néléments passent à la fin.",
      "variants": [
        {
          "statement": "On dispose de `L = [1, 2, 3, 4, 5]`.\nQue vaut `L[2:] + L[:2]` ?",
          "options": [
            {
              "text": "La liste `[3, 4, 5, 1, 2]`",
              "correct": true,
              "feedback": "Bonne réponse : on met la fin de la\nliste (à partir de l'indice $2$)\ndevant ses $2$ premiers éléments.\n"
            },
            {
              "text": "La liste `[4, 5, 1, 2, 3]`",
              "correct": false,
              "feedback": "Erreur : c'est la rotation dans l'autre\nsens, `L[-2:] + L[:-2]`.\n"
            },
            {
              "text": "La liste `[1, 2, 3, 4, 5]`, inchangée",
              "correct": false,
              "feedback": "Erreur : l'ordre des deux morceaux\ncompte. Ici, la fin passe devant.\n"
            },
            {
              "text": "La liste `[3, 4, 5]`",
              "correct": false,
              "feedback": "Erreur : `+` met bout à bout les deux\ntranches, aucun élément n'est perdu.\n"
            }
          ],
          "explanation": "`L[k:] + L[:k]` fait tourner la liste de\n$k$ crans vers la gauche : les $k$ premiers\néléments passent à la fin."
        },
        {
          "statement": "On dispose de `L = [10, 20, 30, 40]`.\nQue vaut `L[1:] + L[:1]` ?",
          "options": [
            {
              "text": "La liste `[20, 30, 40, 10]`",
              "correct": true,
              "feedback": "Bonne réponse : on met la fin de la\nliste (à partir de l'indice $1$)\ndevant ses $1$ premiers éléments.\n"
            },
            {
              "text": "La liste `[40, 10, 20, 30]`",
              "correct": false,
              "feedback": "Erreur : c'est la rotation dans l'autre\nsens, `L[-1:] + L[:-1]`.\n"
            },
            {
              "text": "La liste `[10, 20, 30, 40]`, inchangée",
              "correct": false,
              "feedback": "Erreur : l'ordre des deux morceaux\ncompte. Ici, la fin passe devant.\n"
            },
            {
              "text": "La liste `[20, 30, 40]`",
              "correct": false,
              "feedback": "Erreur : `+` met bout à bout les deux\ntranches, aucun élément n'est perdu.\n"
            }
          ],
          "explanation": "`L[k:] + L[:k]` fait tourner la liste de\n$k$ crans vers la gauche : les $k$ premiers\néléments passent à la fin."
        },
        {
          "statement": "On dispose de `L = [7, 8, 9, 10, 11, 12]`.\nQue vaut `L[4:] + L[:4]` ?",
          "options": [
            {
              "text": "La liste `[11, 12, 7, 8, 9, 10]`",
              "correct": true,
              "feedback": "Bonne réponse : on met la fin de la\nliste (à partir de l'indice $4$)\ndevant ses $4$ premiers éléments.\n"
            },
            {
              "text": "La liste `[9, 10, 11, 12, 7, 8]`",
              "correct": false,
              "feedback": "Erreur : c'est la rotation dans l'autre\nsens, `L[-4:] + L[:-4]`.\n"
            },
            {
              "text": "La liste `[7, 8, 9, 10, 11, 12]`, inchangée",
              "correct": false,
              "feedback": "Erreur : l'ordre des deux morceaux\ncompte. Ici, la fin passe devant.\n"
            },
            {
              "text": "La liste `[11, 12]`",
              "correct": false,
              "feedback": "Erreur : `+` met bout à bout les deux\ntranches, aucun élément n'est perdu.\n"
            }
          ],
          "explanation": "`L[k:] + L[:k]` fait tourner la liste de\n$k$ crans vers la gauche : les $k$ premiers\néléments passent à la fin."
        }
      ]
    },
    {
      "id": "q74",
      "difficulty": 1,
      "skills": [
        "tranche",
        "tuple",
        "pas"
      ],
      "title": "Tranche d'un tuple avec un pas",
      "partie": "slicing",
      "statement": "On dispose de `T = ('a', 'b', 'c', 'd', 'e')`.\nQue vaut `T[::2]` ?",
      "options": [
        {
          "text": "Le tuple `('a', 'c', 'e')`",
          "correct": true,
          "feedback": "Bonne réponse : un élément sur deux à\npartir de l'indice $0$, et le résultat\nest un tuple, comme `T`.\n"
        },
        {
          "text": "La liste `['a', 'c', 'e']`",
          "correct": false,
          "feedback": "Erreur : une tranche garde le type de\ndépart. Sur un tuple, elle renvoie un\ntuple.\n"
        },
        {
          "text": "Le tuple `('b', 'd')`",
          "correct": false,
          "feedback": "Erreur : ce sont les indices impairs,\n`T[1::2]`. Sans début, on part de\nl'indice $0$.\n"
        },
        {
          "text": "Le tuple `('a', 'b')`, les deux premiers éléments",
          "correct": false,
          "feedback": "Erreur : le $2$ est ici le pas, pas la\nfin : il faudrait `T[:2]`.\n"
        }
      ],
      "explanation": "Les tuples acceptent toutes les tranches,\npas compris : seule l'affectation d'une\ntranche leur est interdite."
    }
  ]
}