{
  "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,\nméthodes principales (`append`, `pop`, `len`, etc.),\ndifférences entre tuples et listes, cas d'usage,\ndécoupage (*slicing*), affectation multiple.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition-liste"
      ],
      "title": "Création d'une liste",
      "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",
      "statement": "Comment crée-t-on un **tuple** en\nPython ?",
      "options": [
        {
          "text": "Avec la syntaxe `t = tuple[1, 2, 3]`",
          "correct": false,
          "feedback": "Cette syntaxe est invalide en\nPython.\n"
        },
        {
          "text": "Avec `t = ()`, `t = (1, 2, 3)`, ou même `t = 1, 2, 3`",
          "correct": true,
          "feedback": "Les parenthèses sont souvent\nfacultatives ; ce sont les\nvirgules qui définissent un\ntuple. Pour un tuple à un\nseul élément, la virgule est\nobligatoire : `t = (1,)`.\n"
        },
        {
          "text": "Avec la syntaxe `t = {1, 2, 3}`",
          "correct": false,
          "feedback": "Les accolades autour de\nplusieurs éléments séparés\npar des virgules créent un\n**ensemble** (`set`), pas un\ntuple.\n"
        },
        {
          "text": "Avec la syntaxe `t = []`",
          "correct": false,
          "feedback": "Les crochets servent à créer\nune liste, et non un tuple.\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é",
      "statement": "Quelle est la **différence\nfondamentale** entre un tuple et\nune liste ?",
      "options": [
        {
          "text": "Un tuple a forcément 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",
          "correct": false,
          "feedback": "La différence porte\nprécisément sur la mutabilité,\ncomme expliqué dans la bonne\nréponse.\n"
        },
        {
          "text": "Une liste est mutable (modifiable après sa création), tandis qu'un tuple est immuable (figé une fois créé)",
          "correct": true,
          "feedback": "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": "La couleur d'affichage dans l'éditeur",
          "correct": false,
          "feedback": "Cette description littérale\nn'a aucun rapport avec la\nnotion technique. La\ndiffé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",
      "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": "Une indexation négative est aussi\npossible : `liste[-1]` désigne le\ndernier élément, `liste[-2]`\nl'avant-dernier, et ainsi de\nsuite. C'est très utile pour\naccéder à la fin d'une liste sans\navoir à connaître sa longueur."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "longueur"
      ],
      "title": "Longueur d'une séquence",
      "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",
      "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\nutilise `liste.extend([4, 5, 6])`."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "parcours"
      ],
      "title": "Parcours d'une liste",
      "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)):`.\nPour parcourir avec à la fois\nl'indice et la valeur :\n`for i, x in enumerate(liste):`."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "in-operator"
      ],
      "title": "Test d'appartenance",
      "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\nbooléen indiquant la\nprésence ou l'absence de\nl'élément. Sur une liste,\ncette opération s'exécute en\n$O(n)$ par parcours.\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": "Deux méthodes utiles existent\npar ailleurs : `liste.index(42)`\nrenvoie l'indice de la première\noccurrence, ou lève une\nexception `ValueError` si\nl'élément n'est pas présent ;\n`liste.count(42)` compte le\nnombre d'occurrences."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "comparaison-tuple-liste"
      ],
      "title": "Quand utiliser un tuple ?",
      "statement": "Dans quel cas préfère-t-on un\n**tuple** à une liste ?",
      "options": [
        {
          "text": "Uniquement pour stocker des nombres",
          "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, paires nom-valeur",
          "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",
      "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\ntrois : 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`."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "slice"
      ],
      "title": "Découpage d'une liste",
      "statement": "Que renvoie l'expression\n`[1, 2, 3, 4, 5][1:4]` ?",
      "options": [
        {
          "text": "La liste `[1, 2, 3]`",
          "correct": false,
          "feedback": "L'indice $1$ commence au\ndeuxième élément. La borne\nbasse est inclusive, donc\non commence par l'élément à\nl'indice $1$.\n"
        },
        {
          "text": "La liste `[2, 3, 4]`",
          "correct": true,
          "feedback": "La syntaxe `liste[a:b]`\nextrait les éléments de\nl'indice `a` (inclus) à\nl'indice `b` (exclu). Les\nindices $1$, $2$ et $3$\ncorrespondent ici aux\néléments $2$, $3$ et $4$.\n"
        },
        {
          "text": "La liste `[1, 4]`",
          "correct": false,
          "feedback": "Le découpage extrait une\ntranche continue, et non\ndeux éléments isolés.\n"
        },
        {
          "text": "La liste `[2, 3, 4, 5]`",
          "correct": false,
          "feedback": "La borne haute est\n**exclue** par convention.\nOn ne prend donc pas\nl'élément d'indice $4$\n(qui est $5$).\n"
        }
      ],
      "explanation": "Convention Python : la borne\nbasse est incluse, la borne\nhaute est exclue. C'est cohérent\navec `range(a, b)`. La forme\ncomplète du découpage est\n`liste[a:b:pas]`, où le pas est\nfacultatif (par défaut $1$)."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "unpacking"
      ],
      "title": "Affectation multiple",
      "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": "Une variante plus avancée\nutilise l'astérisque pour\ncapturer une partie variable :\n`a, *reste = [1, 2, 3, 4]`\naffecte $a = 1$ et\n`reste = [2, 3, 4]`."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "methodes-liste"
      ],
      "title": "Méthode pop",
      "statement": "Que fait l'appel `liste.pop()` ?",
      "options": [
        {
          "text": "Elle retire et renvoie le dernier élément de la liste, en $O(1)$",
          "correct": true,
          "feedback": "Avec un argument, `pop(i)`\nretire et renvoie l'élément\nd'indice `i`. Cette opération\nest en $O(n)$ pour\n$i \\neq -1$, à cause du\ndécalage des éléments\nsuivants.\n"
        },
        {
          "text": "Elle se contente d'afficher le dernier élément",
          "correct": false,
          "feedback": "La méthode `pop` modifie la\nliste **et** renvoie la\nvaleur retirée.\n"
        },
        {
          "text": "Elle compte le nombre d'éléments de la liste",
          "correct": false,
          "feedback": "Le comptage des éléments se\nfait avec la fonction `len`.\n"
        },
        {
          "text": "Elle trie la liste sur place",
          "correct": false,
          "feedback": "Le tri sur place se fait\navec la méthode `sort`.\n"
        }
      ],
      "explanation": "Combinées, les méthodes `append`\net `pop` permettent d'ajouter\net de retirer des éléments à la\nfin d'une liste. Ces deux\nopérations sont très courantes\npour gérer dynamiquement le\ncontenu d'une liste."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "bug-mutabilite"
      ],
      "title": "Piège de la valeur par défaut mutable",
      "statement": "```python\ndef f(l=[]):\n    l.append(1)\n    return l\n\nprint(f())\nprint(f())\n```\nQuel est le résultat de l'exécution\nde ce code ?",
      "options": [
        {
          "text": "```\n[1]\n[1]\n```\n",
          "correct": false,
          "feedback": "Cette réponse ignore le fait\nque la valeur par défaut est\npartagée entre les appels\nsuccessifs.\n"
        },
        {
          "text": "```\n[]\n[]\n```\n",
          "correct": false,
          "feedback": "Cette réponse ignore les\nappels à `append`, qui\najoutent bien un élément\nà chaque appel de `f`.\n"
        },
        {
          "text": "```\n[1]\n[1, 1]\n```\n",
          "correct": true,
          "feedback": "La liste par défaut est créée\n**une seule fois**, à la\ndéfinition de la fonction.\nLes appels successifs\npartagent donc la même\nliste. Pour éviter ce\npiège, on écrit `l=None`,\npuis dans la fonction\n`if l is None: l = []`.\n"
        },
        {
          "text": "Une exception est levée",
          "correct": false,
          "feedback": "Le code est parfaitement\nvalide ; aucune exception\nn'est levée.\n"
        }
      ],
      "explanation": "C'est l'un des plus célèbres\npièges de Python. Il découle\ndirectement de la mutabilité\ndes listes et de la manière\ndont Python évalue les valeurs\npar défaut."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "tri"
      ],
      "title": "Méthodes sort et sorted",
      "statement": "Quelle est la différence entre\n`liste.sort()` et `sorted(liste)` ?",
      "options": [
        {
          "text": "La méthode `sort` est plus lente",
          "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",
          "correct": false,
          "feedback": "Une différence importante\nexiste, comme expliqué\ndans la bonne réponse.\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": "La méthode `sort` modifie la liste sur place (et renvoie `None`), tandis que la fonction `sorted` renvoie une nouvelle liste triée sans modifier l'originale",
          "correct": true,
          "feedback": "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": "Convention Python : les verbes\nà la voix passive (`sorted`,\n`reversed`) renvoient une\nversion transformée sans\nmodifier l'original ; les\nnoms d'action (`sort`,\n`reverse`) modifient sur\nplace."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "comprehension-liste"
      ],
      "title": "Compréhension de liste",
      "statement": "Quel code crée la liste des\ncarrés des entiers de $1$ à $10$ ?",
      "options": [
        {
          "text": "`for i in range(1, 11): liste.append(i  2)`",
          "correct": false,
          "feedback": "Cette construction sur\nplusieurs lignes\nfonctionne, mais la\ncompréhension de liste est\nplus idiomatique.\n"
        },
        {
          "text": "`[i  2 for i in range(1, 11)]`",
          "correct": true,
          "feedback": "C'est une **compréhension de\nliste**, une syntaxe concise\net idiomatique en Python.\n"
        },
        {
          "text": "`(i  2 for i in range(1, 11))`",
          "correct": false,
          "feedback": "Cette syntaxe crée une\n**expression génératrice**,\nparcourable une seule fois,\net non une liste. Avec\n`list(...)` autour, on\nobtiendrait bien une liste.\n"
        },
        {
          "text": "`{i  2 for i in range(1, 11)}`",
          "correct": false,
          "feedback": "Cette syntaxe crée un\n**ensemble** (`set`), pas\nune liste.\n"
        }
      ],
      "explanation": "Quatre formes de compréhension\nexistent : `[]` pour les\nlistes, `{}` pour les\nensembles, `{k: v for ...}`\npour les dictionnaires, et\n`(...)` pour les générateurs.\nToutes sont très utilisées en\nPython idiomatique."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "reference-vs-copie"
      ],
      "title": "Référence et copie",
      "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 utilise\n`b = a.copy()` ou\n`b = 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": [
        "retour-multiple"
      ],
      "title": "Retours multiples par tuple",
      "statement": "Que renvoie cette fonction ?\n```python\ndef min_max(l):\n    return min(l), max(l)\n```",
      "options": [
        {
          "text": "Une chaîne de caractères",
          "correct": false,
          "feedback": "La fonction renvoie deux\nvaleurs numériques, pas\nune chaîne.\n"
        },
        {
          "text": "La valeur `None`",
          "correct": false,
          "feedback": "La fonction renvoie bien\nquelque chose, à savoir un\ntuple.\n"
        },
        {
          "text": "Une liste contenant deux éléments",
          "correct": false,
          "feedback": "La virgule entre les deux\nvaleurs renvoyées crée un\ntuple, et non une liste.\n"
        },
        {
          "text": "Un tuple de la forme `(min, max)`",
          "correct": true,
          "feedback": "La virgule entre deux\nvaleurs construit un tuple.\nC'est l'idiome Python pour\nrenvoyer plusieurs valeurs.\nOn les récupère ensuite par\ndépaquetage :\n`mn, mx = min_max(...)`.\n"
        }
      ],
      "explanation": "Cet idiome est très courant\nen Python. Il évite de\ndéfinir une classe ad hoc\npour transporter deux valeurs\nliées."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "hashable"
      ],
      "title": "Tuple comme clé de dictionnaire",
      "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 repose sur\ndes considérations\ntechniques précises, comme\nexpliqué dans la bonne\nréponse.\n"
        },
        {
          "text": "Parce qu'un tuple est immuable : sa valeur ne peut pas changer après création, ce qui permet d'en calculer une empreinte stable",
          "correct": true,
          "feedback": "Les clés de dictionnaire\ndoivent être stables dans\nle temps. Une liste,\nmutable, ne pourrait pas\nservir de clé : on\npourrait modifier la clé\naprès l'insertion, ce qui\ncasserait la table de\nhachage.\n"
        },
        {
          "text": "Pour des raisons purement historiques",
          "correct": false,
          "feedback": "La raison est strictement\ntechnique, et non\nhistorique.\n"
        }
      ],
      "explanation": "Un objet est dit *hashable*\nlorsqu'il possède une\nempreinte stable et peut\ndonc être placé dans un\nensemble ou utilisé comme\nclé. Les types immuables\nclassiques sont hashables :\nentiers, flottants, chaînes,\ntuples (s'ils ne contiennent\nque des éléments hashables)\net ensembles figés."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "longueur-tuple"
      ],
      "title": "Tuple à un élément",
      "statement": "Que vaut l'expression `len((42))` ?",
      "options": [
        {
          "text": "La valeur $0$",
          "correct": false,
          "feedback": "Aucune valeur de longueur\nn'est calculée ; la\nfonction `len` lève une\nexception sur un entier.\n"
        },
        {
          "text": "Une exception (`TypeError`), car la fonction `len` n'est pas définie sur les entiers",
          "correct": true,
          "feedback": "Pour créer un tuple à un\nseul élément, il faut une\nvirgule : `(42,)`. Sans\ncette virgule, on a juste\nun entier.\n"
        },
        {
          "text": "La valeur $42$",
          "correct": false,
          "feedback": "La fonction `len` ne\nrenvoie pas la valeur de\nson argument.\n"
        },
        {
          "text": "La valeur $1$",
          "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": [
        "trace-pop"
      ],
      "title": "Trace de pop",
      "statement": "```python\nl = [10, 20, 30]\nx = l.pop(0)\nprint(x, l)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "La sortie `30 [10, 20]`",
          "correct": false,
          "feedback": "Sans argument, `pop()`\nretire le dernier\nélément. Mais ici, on a\nprécisé l'indice $0$ :\nc'est donc le premier\nélément qui est retiré.\n"
        },
        {
          "text": "Une exception est levée",
          "correct": false,
          "feedback": "L'indice $0$ est valide\npour une liste de trois\néléments.\n"
        },
        {
          "text": "La sortie `10 [10, 20, 30]`",
          "correct": false,
          "feedback": "La méthode `pop` modifie\nla liste sur place,\ncontrairement à ce que\nsuggère cette réponse.\n"
        },
        {
          "text": "La sortie `10 [20, 30]`",
          "correct": true,
          "feedback": "L'appel `pop(0)` retire et\nrenvoie l'élément d'indice\n$0$, c'est-à-dire $10$. La\nliste devient alors\n`[20, 30]`.\n"
        }
      ],
      "explanation": "Performance : `pop(-1)` est\nen $O(1)$, alors que\n`pop(0)` est en $O(n)$ à\ncause du décalage de tous\nles éléments. Mieux vaut\ndonc retirer les éléments\npar la fin de la liste\nlorsque c'est possible."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "aliasing-piege"
      ],
      "title": "Liste de listes",
      "statement": "```python\ngrille = [[0] * 3] * 3\ngrille[0][0] = 1\nprint(grille)\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "`[[1, 0, 0], [1, 0, 0], [1, 0, 0]]`",
          "correct": true,
          "feedback": "L'expression `[[0]*3]*3`\ncrée trois références\nvers la même sous-liste.\nModifier une sous-liste\nrevient donc à modifier\nles trois. La solution\nconsiste à utiliser une\ncompréhension de liste :\n`[[0] * 3 for _ in range(3)]`.\n"
        },
        {
          "text": "Une exception est levée",
          "correct": false,
          "feedback": "Le code est parfaitement\nvalide.\n"
        },
        {
          "text": "`[[1, 0, 0], [0, 0, 0], [0, 0, 0]]`",
          "correct": false,
          "feedback": "Cette réponse ignore que\nles trois sous-listes\nréférencent le même objet.\n"
        },
        {
          "text": "`[[0, 0, 0], [0, 0, 0], [0, 0, 0]]`",
          "correct": false,
          "feedback": "On a affecté\n`grille[0][0] = 1`, donc\nla valeur $1$ apparaît\nquelque part dans le\nrésultat.\n"
        }
      ],
      "explanation": "C'est un piège classique. La\nbonne pratique consiste à\nutiliser une compréhension de\nliste pour créer chaque\nsous-liste indépendamment."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "methodes-liste-piege"
      ],
      "title": "Méthodes append et extend",
      "statement": "Quelle est la différence entre\nles méthodes `append` et `extend`\nd'une liste ?",
      "options": [
        {
          "text": "La méthode `append(x)` ajoute x comme un seul élément ; la méthode `extend(iterable)` ajoute chaque élément de l'itérable un par un",
          "correct": true,
          "feedback": "Par exemple,\n`[1].append([2, 3])` donne\n`[1, [2, 3]]`, alors que\n`[1].extend([2, 3])`\ndonne `[1, 2, 3]`. La\ndistinction est subtile\nmais importante.\n"
        },
        {
          "text": "Aucune différence notable",
          "correct": false,
          "feedback": "Une différence importante\nexiste, comme expliqué\ndans la bonne réponse.\n"
        },
        {
          "text": "La méthode `append` est plus rapide qu'`extend`",
          "correct": false,
          "feedback": "Les performances sont\néquivalentes pour ajouter\nun élément. La distinction\nporte sur le sens de\nl'opération.\n"
        },
        {
          "text": "La méthode `extend` n'existe pas en Python",
          "correct": false,
          "feedback": "Cette méthode existe bel\net bien et fait partie de\nl'interface standard des\nlistes.\n"
        }
      ],
      "explanation": "Une autre forme équivalente :\n`liste += iterable` revient\nà `liste.extend(iterable)`."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "liste-vs-tuple-perf"
      ],
      "title": "Tuples pour les constantes",
      "statement": "Pour stocker des **constantes**\nque l'on ne modifiera jamais,\nquelle structure est la plus\nindiquée ?",
      "options": [
        {
          "text": "Un dictionnaire",
          "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, qui signale clairement l'immuabilité, est légèrement plus rapide à créer et consomme un peu moins de mémoire",
          "correct": true,
          "feedback": "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",
          "correct": false,
          "feedback": "Une liste fonctionnerait,\nmais on perdrait\nl'information précieuse\nde l'immuabilité, qui\naide à la lecture du\ncode.\n"
        },
        {
          "text": "Une chaîne de caractères",
          "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",
      "statement": "Parmi les affirmations suivantes\nsur les tuples et les listes,\nlaquelle est **fausse** ?",
      "options": [
        {
          "text": "Les deux structures supportent l'indexation et le découpage",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte : on peut écrire\n`l[0]`, `l[1:3]` aussi\nbien sur une liste que\nsur un tuple.\n"
        },
        {
          "text": "Les listes peuvent être utilisées comme clés de dictionnaire",
          "correct": true,
          "feedback": "Cette affirmation est\nfausse (donc c'est la\nbonne réponse). Les\nlistes sont mutables,\ndonc non hashables, et\nne peuvent pas servir\nde clé. Seuls les\ntuples (à condition que\ntous leurs éléments\nsoient hashables)\npeuvent l'être.\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": [
        "slicing",
        "pas",
        "inversion"
      ],
      "title": "Renverser une liste",
      "statement": "On dispose d'une liste `t = [10, 20, 30, 40, 50]`.\nQue vaut `t[::-1]` ?",
      "options": [
        {
          "text": "`[50, 40, 30, 20, 10]`",
          "correct": true,
          "feedback": "Bonne réponse : la syntaxe `[::-1]` produit une\nnouvelle liste contenant les éléments de `t`\ndans l'ordre inverse. La liste d'origine n'est\npas modifiée.\n"
        },
        {
          "text": "`[10, 20, 30, 40, 50]`",
          "correct": false,
          "feedback": "Erreur : ce serait la liste d'origine. Le pas\nnégatif `-1` indique au contraire de parcourir\nla séquence dans le sens inverse.\n"
        },
        {
          "text": "Une erreur d'indice",
          "correct": false,
          "feedback": "Erreur : la syntaxe est valide. Le découpage\n`[debut:fin:pas]` accepte parfaitement un pas\nnégatif lorsqu'on souhaite parcourir la\nséquence à l'envers.\n"
        },
        {
          "text": "`[]`",
          "correct": false,
          "feedback": "Erreur : la liste produite n'est pas vide. Le\npas $-1$ signifie « parcours en arrière », pas\n« efface ».\n"
        }
      ],
      "explanation": "Le découpage généralisé `liste[debut:fin:pas]`\npermet de nombreux traitements rapides :\n`t[::-1]` (inversion), `t[::2]` (un élément sur\ndeux), `t[1::2]` (à partir de l'indice $1$, un sur\ndeux), `t[-3:]` (les trois derniers). Tous\nproduisent une **nouvelle** liste sans modifier\nl'originale."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "methodes",
        "count",
        "index"
      ],
      "title": "Compter et trouver dans une liste",
      "statement": "Soit `t = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]`. Que\nrenvoient successivement les expressions\n`t.count(5)` et `t.index(5)` ?",
      "options": [
        {
          "text": "`5` puis `3`",
          "correct": false,
          "feedback": "Erreur : confusion entre les rôles. La méthode\n`count` répond à « combien ? » (ici trois fois)\net `index` à « où ? » (ici à l'indice $4$).\n"
        },
        {
          "text": "`3` puis `4`",
          "correct": true,
          "feedback": "Bonne réponse : `t.count(5)` compte le nombre\nd'occurrences de $5$ dans la liste, il y en a\ntrois (indices $4$, $8$ et $10$).\n`t.index(5)` renvoie l'indice de la **première**\noccurrence, ici $4$ (l'indexation commence à\nzéro).\n"
        },
        {
          "text": "`5` puis `5`",
          "correct": false,
          "feedback": "Erreur : la valeur $5$ est mentionnée trois\nfois et son premier indice n'est pas $5$. Bien\ndistinguer `count` (combien d'occurrences) de\n`index` (à quelle position).\n"
        },
        {
          "text": "`3` puis `5`",
          "correct": false,
          "feedback": "Erreur : `count` est correct ($3$ occurrences),\nmais `index` renvoie l'indice du **premier**\n$5$, pas la valeur $5$. La première occurrence\nse trouve en position $4$.\n"
        }
      ],
      "explanation": "Méthodes utiles sur les listes : `count(x)` (nombre\nd'occurrences), `index(x)` (premier indice de `x`,\nlève `ValueError` si absent), `remove(x)` (supprime\nla première occurrence). Pour vérifier la présence\nsans déclencher d'erreur, préférer `if x in t:`."
    },
    {
      "id": "q28",
      "difficulty": 2,
      "skills": [
        "append",
        "extend",
        "concatenation"
      ],
      "title": "append, extend et concaténation",
      "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.extend(b)`, qui ajoute les éléments de `b`\nun par un. La méthode `append`, elle, ajoute\nson paramètre comme un **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": "Trois opérations à distinguer : `a.append(x)`\najoute `x` comme un seul élément ; `a.extend(b)`\najoute les éléments de `b` un par un (équivalent\nà `a += b`) ; `a + b` produit une **nouvelle**\nliste sans modifier ni `a` ni `b`. Confondre les\ntrois est l'une des erreurs les plus fréquentes\nen Python."
    }
  ]
}