{
  "chapter": {
    "id": "modules",
    "level": "premiere",
    "theme": "Programmation",
    "title": "Modules Python",
    "description": "Notion de module, importation (`import`,\n`from ... import`), modules standards (`math`,\n`random`, `os`, `csv`...), création de ses propres\nmodules, espaces de noms, alias.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Qu'est-ce qu'un module ?",
      "statement": "En Python, qu'est-ce qu'un **module** ?",
      "options": [
        {
          "text": "Un type de données spécialisé dans les calculs mathématiques",
          "correct": false,
          "feedback": "Erreur : un module est un fichier, pas un\ntype de données ; et il peut contenir du\ncode de toute nature, pas seulement du\ncalcul.\n"
        },
        {
          "text": "Un fichier `.py` regroupant du code réutilisable",
          "correct": true,
          "feedback": "Bonne réponse : un module rassemble des\nfonctions ou constantes réutilisables. On\npeut l'importer dans un autre programme\npour utiliser ses fonctions sans les\nrecoder.\n"
        },
        {
          "text": "Une boucle imbriquée exécutée à l'intérieur d'une fonction",
          "correct": false,
          "feedback": "Une boucle imbriquée est\nune construction de\ncontrôle interne à une\nfonction. Un module est\nau contraire une unité\nd'organisation du code,\nstockée dans un fichier.\n"
        },
        {
          "text": "Une variable globale accessible dans tout le programme",
          "correct": false,
          "feedback": "Une variable globale est\nune variable accessible\ndans tout un programme.\nUn module, lui, est une\nunité d'organisation du\ncode, contenue dans un\nfichier source distinct.\n"
        }
      ],
      "explanation": "La modularité est un principe central en génie\nlogiciel : organiser le code en unités\ncohérentes et réutilisables, plutôt qu'un seul\ngros fichier."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "import"
      ],
      "title": "Import",
      "statement": "Comment **importer** le module `math` en Python ?",
      "options": [
        {
          "text": "`require math`",
          "correct": false,
          "feedback": "Erreur : c'est la syntaxe JavaScript / Ruby.\n"
        },
        {
          "text": "`use math`",
          "correct": false,
          "feedback": "Erreur : c'est la syntaxe Perl.\n"
        },
        {
          "text": "`include math`",
          "correct": false,
          "feedback": "Erreur : c'est la syntaxe C/C++.\n"
        },
        {
          "text": "`import math`",
          "correct": true,
          "feedback": "Bonne réponse : l'instruction `import` rend\naccessible le contenu du module via le\npréfixe `math.` (ex. `math.sqrt(2)`).\n"
        }
      ],
      "explanation": "`import math` charge le module et le rend\naccessible sous le nom `math`. Pour des\nfonctions précises sans préfixe :\n`from math import sqrt`."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "usage"
      ],
      "title": "Utilisation après import",
      "statement": "Après `import math`, comment appelle-t-on la\nfonction `sqrt` (racine carrée) ?",
      "options": [
        {
          "text": "`math->sqrt(2)`",
          "correct": false,
          "feedback": "Erreur : `->` est la syntaxe Perl/PHP.\n"
        },
        {
          "text": "`math.sqrt(2)`",
          "correct": true,
          "feedback": "Bonne réponse : on utilise le préfixe\n`module.fonction`.\n"
        },
        {
          "text": "`sqrt(2)`",
          "correct": false,
          "feedback": "Erreur : sans préfixe `math.`, Python ne\nconnaît pas `sqrt`.\n"
        },
        {
          "text": "`math::sqrt(2)`",
          "correct": false,
          "feedback": "Erreur : `::` est la syntaxe C++ ou Ruby.\n"
        }
      ],
      "explanation": "Préfixe = **espace de noms**. Évite les\nconflits si plusieurs modules ont des\nfonctions de même nom."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "from-import"
      ],
      "title": "from import",
      "statement": "Que fait `from math import sqrt` ?",
      "options": [
        {
          "text": "Importe l'intégralité du module `math` dans le programme",
          "correct": false,
          "feedback": "Erreur : on importe seulement `sqrt`.\n"
        },
        {
          "text": "Importe seulement `sqrt`, appelable directement",
          "correct": true,
          "feedback": "Bonne réponse : on écrit alors `sqrt(2)`\nsans préfixe. Utile quand on n'a besoin\nque de quelques fonctions et qu'on veut\néviter le préfixe.\n"
        },
        {
          "text": "Provoque une erreur, `sqrt` devant rester préfixé",
          "correct": false,
          "feedback": "Erreur : la syntaxe est parfaitement\nvalide ; c'est même elle qui permet de se\npasser du préfixe `math.`.\n"
        },
        {
          "text": "Renomme le module `math` en `sqrt` pour la suite",
          "correct": false,
          "feedback": "Le renommage à\nl'importation utilise\nle mot-clé `as`, par\nexemple `import math as\nm`. La syntaxe `from ...\nimport` ne renomme rien,\nelle expose un symbole\nprécis.\n"
        }
      ],
      "explanation": "Variantes : `from math import sqrt, pi` (deux\nnoms) ou `from math import *` (tout, mais à\néviter, pollue l'espace de noms)."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "math-pi"
      ],
      "title": "Constante pi",
      "statement": "Comment obtenir la valeur de π en Python ?",
      "options": [
        {
          "text": "`math.PI`",
          "correct": false,
          "feedback": "Erreur : Python distingue majuscules et\nminuscules ; `math.PI` n'existe pas\n(`AttributeError`). La constante s'écrit\n`math.pi`.\n"
        },
        {
          "text": "`math.pi`",
          "correct": true,
          "feedback": "Bonne réponse : après `import math`,\n`math.pi` vaut environ 3.141592653589793,\nla meilleure approximation flottante\ndisponible.\n"
        },
        {
          "text": "`Math.pi`",
          "correct": false,
          "feedback": "Erreur : `Math` avec une majuscule est\nl'objet de JavaScript ; en Python le module\ns'appelle `math` (`NameError` sinon).\n"
        },
        {
          "text": "`math.pi()`",
          "correct": false,
          "feedback": "Erreur : `math.pi` est une constante (un\nflottant), pas une fonction : les\nparenthèses provoquent `TypeError`\n(« 'float' object is not callable »).\n"
        }
      ],
      "explanation": "Constantes utiles dans `math` : `math.pi`,\n`math.e`, `math.inf`, `math.nan`."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "random"
      ],
      "title": "Module random",
      "statement": "Quel module de la bibliothèque standard permet\nde générer des nombres aléatoires ?",
      "options": [
        {
          "text": "`numpy`",
          "correct": false,
          "feedback": "Existe et a un sous-module `random`, mais\nce n'est pas la bibliothèque **standard**\n(à installer séparément).\n"
        },
        {
          "text": "`math`",
          "correct": false,
          "feedback": "Erreur : `math` est pour les fonctions\nmathématiques classiques.\n"
        },
        {
          "text": "`random`",
          "correct": true,
          "feedback": "Bonne réponse : `random.randint(a, b)`,\n`random.choice(liste)`, `random.shuffle(...)`,\netc.\n"
        },
        {
          "text": "`stochastic`",
          "correct": false,
          "feedback": "Erreur : pas de module standard de ce nom.\n"
        }
      ],
      "explanation": "`random` est très utilisé en NSI Première\npour les jeux, les simulations, le tirage\naléatoire. Pour des usages cryptographiques,\npréférer `secrets` (plus sûr)."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "usage-randint"
      ],
      "title": "Tirage aléatoire",
      "statement": "Que fait `random.randint(1, 6)` ?",
      "options": [
        {
          "text": "Renvoie 6 (le maximum)",
          "correct": false,
          "feedback": "Erreur : c'est aléatoire, pas\nsystématiquement le max.\n"
        },
        {
          "text": "Renvoie une liste de 6 éléments",
          "correct": false,
          "feedback": "Erreur : un seul entier.\n"
        },
        {
          "text": "Renvoie un nombre flottant entre 1 et 6",
          "correct": false,
          "feedback": "Erreur : `randint` renvoie un **entier**.\n"
        },
        {
          "text": "Renvoie un entier aléatoire entre 1 et 6, inclus",
          "correct": true,
          "feedback": "Bonne réponse : simule un dé à 6 faces.\nAttention : contrairement à `range`, les\ndeux bornes sont **incluses**.\n"
        }
      ],
      "explanation": "Distinction subtile : `random.randint(a, b)`\n→ bornes **incluses** ; `random.randrange(a,\nb)` → b **exclue** (comme `range`)."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "import-as"
      ],
      "title": "Alias avec as",
      "statement": "Que fait `import math as m` ?",
      "options": [
        {
          "text": "Importe deux modules, `math` et `m`",
          "correct": false,
          "feedback": "Erreur : un seul module est chargé ; `m` n'est\nqu'un second nom pour le désigner.\n"
        },
        {
          "text": "Importe `math` mais le rend accessible sous le nom `m`",
          "correct": true,
          "feedback": "Bonne réponse : `as m` crée un **alias**. On\nécrit ensuite `m.sqrt(2)` ; le nom `math` seul\nn'est pas défini et `math.sqrt(2)` lève\n`NameError`. Pratique pour raccourcir un nom\nde module long.\n"
        },
        {
          "text": "Provoque une erreur de syntaxe à cause du mot `as`",
          "correct": false,
          "feedback": "Erreur : la syntaxe `import x as y` est\nvalide et courante.\n"
        },
        {
          "text": "Importe seulement la fonction `m` du module `math`",
          "correct": false,
          "feedback": "Erreur : c'est tout `math` qui est importé,\nsimplement accessible via `m`. Importer un\nseul nom s'écrit `from math import sqrt`.\n"
        }
      ],
      "explanation": "L'alias ne change rien au contenu du module : il\nraccourcit seulement le préfixe à écrire devant\nchaque fonction, ce qui allège le code quand le\nmodule est utilisé souvent."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "bibliotheque-standard"
      ],
      "title": "Bibliothèque standard",
      "statement": "Que signifie « bibliothèque standard de Python » ?",
      "options": [
        {
          "text": "Une norme ISO obligatoire",
          "correct": false,
          "feedback": "Erreur : pas de normalisation officielle.\n"
        },
        {
          "text": "La documentation de référence officielle du langage",
          "correct": false,
          "feedback": "Confusion : la bibliothèque standard est un\n**ensemble de modules**, pas la doc.\n"
        },
        {
          "text": "Les modules livrés avec Python, sans installation",
          "correct": true,
          "feedback": "Bonne réponse : `math`, `random`, `os`,\n`sys`, `csv`, `json`, `datetime`,\n`urllib`... tous prêts à l'emploi. C'est\nl'une des grandes forces de Python\n(« batteries included »).\n"
        },
        {
          "text": "La bibliothèque physique d'Anthropic",
          "correct": false,
          "feedback": "La bibliothèque standard\nde Python est un ensemble\nde modules logiciels\nmaintenu par la Python\nSoftware Foundation. Elle\nn'a aucun lien avec\nAnthropic ni avec une\ncollection physique.\n"
        }
      ],
      "explanation": "Pour les modules **externes** (numpy, pandas,\nmatplotlib, requests...), il faut installer\navec `pip install nom_du_module`."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "exemple-pratique"
      ],
      "title": "Exemple complet",
      "statement": "```python\nimport math\nprint(math.sqrt(16))\n```\nQue produit ce code ?",
      "options": [
        {
          "text": "Affiche `16.0`",
          "correct": false,
          "feedback": "Erreur : `sqrt` calcule la **racine**, pas\nl'identité.\n"
        },
        {
          "text": "Affiche `4.0`",
          "correct": true,
          "feedback": "Bonne réponse : `math.sqrt(16)` = 4.0\n(flottant, même pour un carré parfait).\n"
        },
        {
          "text": "Lève une erreur",
          "correct": false,
          "feedback": "Erreur : code valide.\n"
        },
        {
          "text": "Affiche `4`",
          "correct": false,
          "feedback": "Pas tout à fait : `sqrt` renvoie un\n**flottant** (`4.0`), pas un entier.\n"
        }
      ],
      "explanation": "Pour un entier, utiliser `int(math.sqrt(16))`\nou `math.isqrt(16)` (entier directement, sans\npasser par un flottant)."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "namespace"
      ],
      "title": "Espace de noms",
      "statement": "Pourquoi est-il **déconseillé** d'utiliser\n`from math import *` ?",
      "options": [
        {
          "text": "Parce que ce mode d'importation est nettement trop lent à l'exécution",
          "correct": false,
          "feedback": "Erreur : performance équivalente.\n"
        },
        {
          "text": "Parce que les noms importés peuvent masquer des fonctions existantes",
          "correct": true,
          "feedback": "Bonne réponse : tous les noms du module\nsont importés d'un coup (collisions\npossibles). Par exemple, si vous aviez une\nvariable `pow`, elle serait écrasée par\n`math.pow`. Source de bugs subtils.\n"
        },
        {
          "text": "Parce que cette syntaxe est interdite depuis Python 3",
          "correct": false,
          "feedback": "Erreur : la syntaxe reste parfaitement\nautorisée, elle est seulement déconseillée.\n"
        },
        {
          "text": "Parce que seule la première fonction du module est importée",
          "correct": false,
          "feedback": "Erreur : c'est tout l'inverse, l'étoile\nimporte **tous** les noms publics du\nmodule d'un seul coup.\n"
        }
      ],
      "explanation": "Bonne pratique : `import math` (préfixe) ou\n`from math import sqrt, pi` (sélectif).\nÉviter `from x import *` sauf pour des\nmodules conçus pour cela."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "creer-module"
      ],
      "title": "Créer son module",
      "statement": "Comment créer son propre module\n`mes_outils.py` et l'importer ?",
      "options": [
        {
          "text": "Il faut placer un mot-clé `module` au début du fichier source",
          "correct": false,
          "feedback": "Erreur : aucun mot-clé spécial n'est\nnécessaire, tout fichier `.py` est\nd'office un module.\n"
        },
        {
          "text": "Il faut compiler `mes_outils.py` avant de pouvoir l'importer",
          "correct": false,
          "feedback": "Erreur : Python est interprété, aucune\ncompilation explicite n'est demandée au\nprogrammeur.\n"
        },
        {
          "text": "Écrire le fichier, puis faire `import mes_outils`",
          "correct": true,
          "feedback": "Bonne réponse : tout fichier `.py` est un\nmodule. On crée `mes_outils.py` avec ses\nfonctions, et `import mes_outils` suffit.\nAucune déclaration spéciale n'est\nnécessaire.\n"
        },
        {
          "text": "Il faut d'abord publier le module sur PyPI pour l'importer",
          "correct": false,
          "feedback": "Erreur : PyPI ne sert qu'au partage\npublic. Localement, le fichier suffit.\n"
        }
      ],
      "explanation": "Cette simplicité est l'un des grands atouts\nde Python pour la modularité. Un projet est\nnaturellement organisé en plusieurs fichiers\n`.py`."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "import-once"
      ],
      "title": "Import une seule fois",
      "statement": "Si l'on écrit `import math` deux fois dans le\nmême programme, que se passe-t-il ?",
      "options": [
        {
          "text": "Cela lève une erreur",
          "correct": false,
          "feedback": "Erreur : pas d'erreur.\n"
        },
        {
          "text": "Le module est entièrement dupliqué une seconde fois en mémoire",
          "correct": false,
          "feedback": "Erreur : un seul exemplaire en mémoire.\n"
        },
        {
          "text": "Le module n'est chargé qu'une fois ; le second import ne fait rien",
          "correct": true,
          "feedback": "Bonne réponse : Python tient un cache des\nmodules déjà chargés (`sys.modules`).\nPerformance optimale.\n"
        },
        {
          "text": "Le module est rechargé à chaque fois (lent)",
          "correct": false,
          "feedback": "Erreur : Python optimise.\n"
        }
      ],
      "explanation": "Pour vraiment recharger un module modifié\n(rare), utiliser `importlib.reload`. Sinon,\nle cache simplifie la vie."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "random-choice"
      ],
      "title": "Choix aléatoire",
      "statement": "Quelle fonction du module `random` permet de\nchoisir un élément au hasard dans une liste ?",
      "options": [
        {
          "text": "`random.select(liste)`",
          "correct": false,
          "feedback": "Erreur : pas de fonction de ce nom.\n"
        },
        {
          "text": "`random.random(liste)`",
          "correct": false,
          "feedback": "Erreur : `random.random()` renvoie un\nflottant entre 0 et 1, sans paramètre.\n"
        },
        {
          "text": "`random.choice(liste)`",
          "correct": true,
          "feedback": "Bonne réponse : `choice` renvoie un\nélément aléatoire de la liste (chaque\nélément a la même probabilité d'être\nchoisi).\n"
        },
        {
          "text": "`random.pick(liste)`",
          "correct": false,
          "feedback": "Erreur : la fonction n'a pas ce nom.\n"
        }
      ],
      "explanation": "Autres fonctions utiles : `random.choices`\n(avec poids), `random.sample` (sans\nremise), `random.shuffle` (mélange en place)."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "main"
      ],
      "title": "Bloc principal",
      "statement": "Que signifie l'idiome `if __name__ == \"__main__\":` ?",
      "options": [
        {
          "text": "Que le programme a passé tous ses tests sans erreur",
          "correct": false,
          "feedback": "La présence de cette\ncondition n'apporte\naucune garantie sur la\ncorrection du programme.\nElle distingue uniquement\nun script exécutable d'un\nmodule importable.\n"
        },
        {
          "text": "Que le bloc s'exécute seulement si le fichier est lancé directement",
          "correct": true,
          "feedback": "Bonne réponse : `__name__` vaut\n`\"__main__\"` en exécution directe et le\nnom du module en cas d'import (le bloc est\nalors ignoré). Cet idiome permet d'avoir\ndes fichiers à la fois modules **et**\nscripts.\n"
        },
        {
          "text": "Que le programme s'exécute en mode débogage pas à pas",
          "correct": false,
          "feedback": "Aucun mode de débogage\nn'est associé à cette\nconstruction. La variable\n`__name__` reflète le\ncontexte d'exécution :\nscript lancé directement\nou module importé.\n"
        },
        {
          "text": "Que la fonction nommée `main` doit obligatoirement être définie",
          "correct": false,
          "feedback": "Pas tout à fait : `main` n'est qu'une\nconvention, pas obligatoire.\n"
        }
      ],
      "explanation": "Schéma standard pour rendre un fichier\nréutilisable comme module sans déclencher\nses tests/démos lors de l'import."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "help-doc"
      ],
      "title": "Documentation interactive",
      "statement": "Comment obtenir l'aide d'une fonction d'un\nmodule en mode interactif ?",
      "options": [
        {
          "text": "`help(math.sqrt)`",
          "correct": true,
          "feedback": "Bonne réponse : la fonction native `help`\naffiche la docstring et les annotations\nde la fonction. Très utile en exploration.\n"
        },
        {
          "text": "`man math.sqrt`",
          "correct": false,
          "feedback": "Erreur : `man` est une commande Unix.\n"
        },
        {
          "text": "`info(math.sqrt)`",
          "correct": false,
          "feedback": "Erreur : pas de fonction `info` en Python.\n"
        },
        {
          "text": "`google math.sqrt`",
          "correct": false,
          "feedback": "Aucune commande `google`\nn'existe dans la console\nPython. La documentation\nintégrée s'obtient avec\nla fonction `help`, qui\naffiche directement la\ndocstring associée.\n"
        }
      ],
      "explanation": "Autre forme utile en interactif : `?math.sqrt`\n(en IPython/Jupyter). Et toujours la\ndocumentation officielle :\ndocs.python.org."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "import-fonction"
      ],
      "title": "Importation sélective",
      "statement": "Quel code utilise `pi` correctement après\n`from math import pi` ?",
      "options": [
        {
          "text": "`math.pi`",
          "correct": false,
          "feedback": "Erreur : avec `from ... import`, on\nn'utilise **pas** le préfixe.\n"
        },
        {
          "text": "`pi`",
          "correct": true,
          "feedback": "Bonne réponse : `pi` est importé\ndirectement dans l'espace de noms local.\nOn peut écrire `2 * pi`.\n"
        },
        {
          "text": "`import.pi`",
          "correct": false,
          "feedback": "Erreur : syntaxe invalide.\n"
        },
        {
          "text": "`from.pi`",
          "correct": false,
          "feedback": "Erreur : syntaxe invalide.\n"
        }
      ],
      "explanation": "`import math` → `math.pi` ; `from math import\npi` → `pi`. Choisir selon ses préférences."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "conflict-noms"
      ],
      "title": "Conflit de noms",
      "statement": "```python\nfrom math import sqrt\n\ndef sqrt(x):\n    return x ** 0.5\n\nprint(sqrt(16))\n```\nQuelle fonction est appelée ?",
      "options": [
        {
          "text": "La fonction `sqrt` importée du module `math`",
          "correct": false,
          "feedback": "Erreur : la définition locale, postérieure\nà l'import, **écrase** le nom importé.\n"
        },
        {
          "text": "Les deux fonctions `sqrt`, appelées l'une après l'autre",
          "correct": false,
          "feedback": "Erreur : un nom ne désigne qu'une seule\nfonction à la fois ; un seul appel a lieu.\n"
        },
        {
          "text": "La fonction `sqrt` définie localement",
          "correct": true,
          "feedback": "Bonne réponse : la dernière définition\ngagne, et le programme affiche `4.0`. Les\ndeux fonctions calculent ici la même chose\n(la racine carrée), donc le résultat est\nidentique, mais en général c'est un piège :\nla définition locale masque silencieusement\nl'import.\n"
        },
        {
          "text": "Aucune, Python lève une erreur de conflit de noms",
          "correct": false,
          "feedback": "Erreur : Python ne signale rien, il écrase\nsilencieusement l'ancien nom. C'est\nprécisément ce qui rend ce piège dangereux.\n"
        }
      ],
      "explanation": "Bonne pratique : éviter de redéfinir des\nnoms importés. Si on doit le faire, l'import\navec préfixe (`import math`) protège."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "random-seed"
      ],
      "title": "Reproductibilité",
      "statement": "Pourquoi utilise-t-on `random.seed(42)` ?",
      "options": [
        {
          "text": "Pour rendre les tirages plus aléatoires",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse, on les rend\ndéterministes.\n"
        },
        {
          "text": "Pour accélérer significativement le générateur de nombres",
          "correct": false,
          "feedback": "Erreur : pas d'effet sur la performance.\n"
        },
        {
          "text": "Pour rendre les tirages reproductibles",
          "correct": true,
          "feedback": "Bonne réponse : même graine, même séquence\nde tirages. Utile pour le débogage, les\ntests, la reproductibilité scientifique.\n"
        },
        {
          "text": "Pour limiter les tirages aux valeurs inférieures à 42",
          "correct": false,
          "feedback": "Erreur : la graine initialise l'état du\ngénérateur, elle ne borne pas les valeurs\nproduites.\n"
        }
      ],
      "explanation": "Important : `random` n'est pas\ncryptographiquement sûr (utiliser `secrets`\npour cela). Mais pour les simulations et\njeux, c'est parfait."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "creer-module",
        "namespace"
      ],
      "title": "Imports d'un module importé",
      "statement": "Le fichier `outils.py` contient :\n\n```python\nimport math\n\ndef aire_disque(r):\n    return math.pi * r ** 2\n```\n\nDans le même dossier, le programme `main.py`\ncontient :\n\n```python\nimport outils\n\nprint(outils.aire_disque(2))\nprint(math.pi)\n```\n\nQue se passe-t-il quand on exécute `main.py` ?",
      "options": [
        {
          "text": "Les deux lignes s'affichent, car `import outils` importe aussi `math`",
          "correct": false,
          "feedback": "Erreur : l'import de `math` a lieu dans\nl'espace de noms d'`outils`, pas dans celui\nde `main.py`. Le nom `math` n'existe pas dans\nle programme principal, et `print(math.pi)`\nlève `NameError`.\n"
        },
        {
          "text": "Le premier affichage échoue, car `main.py` n'a pas importé `math`",
          "correct": false,
          "feedback": "Erreur : `aire_disque` est définie dans\n`outils.py`, où `math` est bien importé. Elle\ns'exécute sans problème et affiche\n`12.566370614359172`. C'est la ligne suivante\nqui échoue.\n"
        },
        {
          "text": "Le premier affichage réussit, puis `print(math.pi)` lève `NameError`",
          "correct": true,
          "feedback": "Bonne réponse : `12.566370614359172` s'affiche,\ncar `aire_disque` trouve `math` dans son propre\nmodule. Puis `main.py` cherche le nom `math`\ndans son espace de noms, où il n'a jamais été\nimporté, et Python lève\n`NameError: name 'math' is not defined`.\n"
        },
        {
          "text": "La ligne `import outils` échoue, car un module ne peut pas en importer un autre",
          "correct": false,
          "feedback": "Erreur : un module peut importer d'autres\nmodules, c'est même courant. `import outils`\nréussit et exécute `import math` à l'intérieur\nd'`outils`.\n"
        }
      ],
      "explanation": "Chaque module a son propre espace de noms. Les\nimports faits dans `outils.py` ne sont visibles que\ndans `outils.py` : pour utiliser `math.pi` dans\n`main.py`, il faut y écrire aussi `import math`."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "pip"
      ],
      "title": "Installer un module externe",
      "statement": "Comment installe-t-on un module externe (par\nexemple `pygame`) qui n'est pas dans la\nbibliothèque standard ?",
      "options": [
        {
          "text": "Avec `pip install pygame` dans le terminal",
          "correct": true,
          "feedback": "Bonne réponse : `pip` est l'outil\nd'installation officiel de Python.\n`pip install nom` télécharge depuis PyPI\n(Python Package Index).\n"
        },
        {
          "text": "Avec l'instruction `download pygame` exécutée dans Python",
          "correct": false,
          "feedback": "Erreur : pas de fonction de ce nom.\n"
        },
        {
          "text": "Avec `import pygame`",
          "correct": false,
          "feedback": "Erreur : `import` charge un module **déjà installé**, ne l'installe pas.\n"
        },
        {
          "text": "Il faut le télécharger manuellement et le copier",
          "correct": false,
          "feedback": "C'était possible historiquement, mais\n`pip` automatise tout cela.\n"
        }
      ],
      "explanation": "`pip` gère aussi les dépendances entre\nmodules. Pour un projet, un fichier\n`requirements.txt` listant les dépendances\nfacilite la reproductibilité."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "structure-package"
      ],
      "title": "Package",
      "statement": "Quelle est la différence entre un **module** et\nun **package** en Python ?",
      "options": [
        {
          "text": "Un module s'exécute plus rapidement qu'un package",
          "correct": false,
          "feedback": "Erreur : il n'y a aucune différence de\nperformance entre les deux.\n"
        },
        {
          "text": "Un package est un module compilé en code machine",
          "correct": false,
          "feedback": "Erreur : aucune compilation en jeu ; la\ndifférence est purement structurelle.\n"
        },
        {
          "text": "Aucune, les deux mots désignent la même chose",
          "correct": false,
          "feedback": "Erreur : ce sont deux notions différentes,\nl'une est un fichier, l'autre un répertoire.\n"
        },
        {
          "text": "Un package est un répertoire regroupant plusieurs modules",
          "correct": true,
          "feedback": "Bonne réponse : un module est un fichier\n`.py`, un package un répertoire qui regroupe\nplusieurs modules. Les grandes bibliothèques\nscientifiques sont organisées ainsi ; on\naccède à un sous-module avec la notation\npointée, `paquet.sousmodule`.\n"
        }
      ],
      "explanation": "En NSI Première, on travaille surtout avec\ndes modules simples. Les packages sont une\nstructuration plus avancée pour les gros\nprojets."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "shadowing"
      ],
      "title": "Pollution du namespace",
      "statement": "Pourquoi le code suivant peut-il être\nproblématique ?\n```python\nfrom random import *\nfrom math import *\n```",
      "options": [
        {
          "text": "Parce que des fonctions de même nom s'écrasent silencieusement",
          "correct": true,
          "feedback": "Bonne réponse : les deux modules ont par\nexemple chacun une fonction `pow`, et le\nsecond import écrase la première sans\nprévenir. Conflit silencieux, source de\nbugs très difficiles à diagnostiquer.\n"
        },
        {
          "text": "Parce que ces importations sont beaucoup trop lentes à charger",
          "correct": false,
          "feedback": "Erreur : le coût de chargement est\nnégligeable, la performance n'est pas le\nproblème.\n"
        },
        {
          "text": "Parce que l'on ne peut pas importer deux modules dans un fichier",
          "correct": false,
          "feedback": "Erreur : on peut importer autant de\nmodules qu'on veut dans un même fichier.\n"
        },
        {
          "text": "Parce que `math` doit impérativement être importé avant `random`",
          "correct": false,
          "feedback": "Erreur : Python n'impose aucun ordre\nd'importation entre modules indépendants.\n"
        }
      ],
      "explanation": "Maxime PEP 20 : « Explicit is better than\nimplicit. » Préférer les imports explicites\npour la traçabilité."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "bonne-pratique"
      ],
      "title": "Ordre des imports",
      "statement": "Dans un fichier Python bien organisé, où\nplacer les `import` ?",
      "options": [
        {
          "text": "À l'intérieur d'une fonction `main` appelée au démarrage du programme",
          "correct": false,
          "feedback": "Possible (chargement paresseux) mais\nexceptionnel. Convention : en haut.\n"
        },
        {
          "text": "À la fin du fichier",
          "correct": false,
          "feedback": "Erreur : tous les noms importés ne seraient\npas disponibles avant.\n"
        },
        {
          "text": "N'importe où dans le fichier",
          "correct": false,
          "feedback": "Techniquement possible, mais déconseillé.\n"
        },
        {
          "text": "En haut du fichier, regroupés (standards / externes / locaux)",
          "correct": true,
          "feedback": "Bonne réponse : convention PEP 8 :\nd'abord les imports standards (`math`,\n`os`...), puis les externes (`numpy`,\n`pandas`...), puis les modules locaux du\nprojet, séparés par une ligne vide.\n"
        }
      ],
      "explanation": "Convention de lecture : voir tout de suite\nles dépendances en haut du fichier.\nBeaucoup d'éditeurs (et d'analyseurs\nstatiques) vérifient cet ordre."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "statement": "Parmi les affirmations suivantes, laquelle est\n**fausse** ?",
      "options": [
        {
          "text": "`from math import pi` importe la constante `pi` sans préfixe",
          "correct": false,
          "feedback": "Vrai : importation sélective.\n"
        },
        {
          "text": "La bibliothèque standard de Python contient des modules pour les fichiers, le réseau, les dates, le hasard, etc.",
          "correct": false,
          "feedback": "Vrai : « batteries included », l'une des\nforces de Python.\n"
        },
        {
          "text": "Tout module importé est systématiquement rechargé à chaque exécution du programme",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : Python met\nen cache les modules dans `sys.modules`.\nUn module n'est chargé **qu'une seule fois** par exécution, même si on l'importe\ndans plusieurs fichiers.\n"
        },
        {
          "text": "`import math` rend les fonctions accessibles via le préfixe `math.`",
          "correct": false,
          "feedback": "Vrai : c'est le rôle de `import`.\n"
        }
      ],
      "explanation": "Le cache de modules garantit la cohérence\n(un seul exemplaire en mémoire) et la\nperformance."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "random",
        "randint"
      ],
      "title": "random.random et random.randint",
      "statement": "Quelle est la différence entre `random.random()` et\n`random.randint(a, b)` ?",
      "options": [
        {
          "text": "Les deux renvoient un entier, `random()` entre 0 et 1 et `randint(a, b)` entre `a` et `b`",
          "correct": false,
          "feedback": "Erreur : la fonction `random.random()` renvoie un\nflottant, pas un entier (un entier entre 0 et 1\nn'aurait d'ailleurs que deux valeurs possibles).\nSeule `randint(a, b)` renvoie un entier.\n"
        },
        {
          "text": "`random()` renvoie un flottant de $[0\\,;\\, 1[$, `randint(a, b)` un entier de $[a\\,;\\, b]$",
          "correct": true,
          "feedback": "Bonne réponse : `random.random()` produit un\nflottant tiré uniformément, strictement\ninférieur à $1$, tandis que\n`random.randint(a, b)` renvoie un entier avec\nles **deux** bornes incluses (alors que la\nplupart des intervalles Python excluent la\nborne droite).\n"
        },
        {
          "text": "`random.randint(a, b)` renvoie un entier de $[a\\,;\\, b[$, borne `b` exclue",
          "correct": false,
          "feedback": "Erreur : c'est précisément la particularité de\n`randint` parmi les fonctions du module : la borne\ndroite est **incluse**. Pour exclure la borne, il\nfaudrait utiliser `random.randrange(a, b)`.\n"
        },
        {
          "text": "`random.random()` renvoie un flottant de $[0\\,;\\, 1]$, borne $1$ comprise",
          "correct": false,
          "feedback": "Erreur : la borne droite est **exclue** :\n`random.random()` peut renvoyer $0$ mais jamais\nexactement $1$, comme la plupart des intervalles\nen Python.\n"
        }
      ],
      "explanation": "Pour mémoriser : `random.random()` $\\to [0\\,;\\, 1[$\n(flottant) ; `random.randint(a, b)` $\\to [a\\,;\\, b]$\n(entier) ; `random.randrange(a, b)` $\\to [a\\,;\\, b[$\n(entier) ; `random.choice(seq)` $\\to$ un élément de\nla séquence. Pour la reproductibilité d'un tirage,\nfixer `random.seed(...)` au début du programme."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "math",
        "fonctions-courantes"
      ],
      "title": "Fonctions du module math",
      "statement": "Parmi les expressions suivantes, laquelle ne renvoie\n**pas** $5$ après l'instruction `import math` ?",
      "options": [
        {
          "text": "`math.ceil(4.3)`",
          "correct": false,
          "feedback": "La fonction `ceil` arrondit vers le haut. Pour\n$4{,}3$, le résultat est $5$.\n"
        },
        {
          "text": "`math.log(100)`",
          "correct": true,
          "feedback": "Bonne réponse : `math.log` calcule le logarithme\n**naturel** par défaut, pas le logarithme\ndécimal. On a $\\ln(100) \\approx 4{,}605$, et non\n$5$. Pour le logarithme en base $10$, il faut\nécrire `math.log10(100)` ou\n`math.log(100, 10)`.\n"
        },
        {
          "text": "`math.sqrt(25)`",
          "correct": false,
          "feedback": "La racine carrée de $25$ vaut bien $5$. Cette\nexpression renvoie donc $5$ (au flottant\n$5{,}0$ près).\n"
        },
        {
          "text": "`math.floor(5.7)`",
          "correct": false,
          "feedback": "La fonction `floor` arrondit vers le bas (partie\nentière inférieure). Pour $5{,}7$, le résultat\nest $5$.\n"
        }
      ],
      "explanation": "Quelques fonctions usuelles du module `math` :\n`sqrt` (racine carrée), `floor` et `ceil` (arrondis\nbas et haut), `log` (logarithme népérien),\n`log10` (logarithme décimal), `exp` (exponentielle),\n`sin`/`cos`/`tan` (trigonométrie en radians).\nConstantes : `math.pi`, `math.e`, `math.inf`."
    },
    {
      "id": "q28",
      "difficulty": 2,
      "skills": [
        "time",
        "mesure-duree"
      ],
      "title": "Mesurer une durée d'exécution",
      "statement": "On souhaite mesurer la durée d'exécution d'une\nportion de code. Quelle structure utilise le module\n`time` de manière correcte ?",
      "options": [
        {
          "text": "```python\nimport time\ndebut = time.time()\n# ... code à mesurer ...\nduree = time.time()\n```\n",
          "correct": false,
          "feedback": "Erreur : il manque la **soustraction** finale.\nIci `duree` contient l'heure de fin (nombre de\nsecondes depuis 1970), pas la durée écoulée :\nil faut écrire `duree = time.time() - debut`.\n"
        },
        {
          "text": "```python\nimport time\ndebut = time.time()\n# ... code à mesurer ...\nduree = debut - time.time()\n```\n",
          "correct": false,
          "feedback": "Erreur : la soustraction est dans le mauvais\nsens. `debut` est antérieur à l'heure de fin,\ndonc `duree` est **négative**. Il faut écrire\n`time.time() - debut`.\n"
        },
        {
          "text": "```python\nimport time\ndebut = time.time()\n# ... code à mesurer ...\nduree = time.time() - time.time()\n```\n",
          "correct": false,
          "feedback": "Erreur : `debut` n'est jamais utilisé. Les deux\nappels à `time.time()` sont quasi simultanés :\n`duree` vaut presque 0 quel que soit le code\nmesuré.\n"
        },
        {
          "text": "```python\nimport time\ndebut = time.time()\n# ... code à mesurer ...\nduree = time.time() - debut\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : `time.time()` renvoie le nombre\nde secondes écoulées depuis le $1^{\\text{er}}$\njanvier $1970$. La différence entre deux\nappels donne la durée écoulée en secondes\n(avec une précision de l'ordre de la\nmicroseconde sur les systèmes modernes).\n"
        }
      ],
      "explanation": "Pour des mesures plus précises ou de très courtes\ndurées, préférer `time.perf_counter()` à\n`time.time()`. Pour comparer la performance de\nplusieurs implémentations, le module `timeit` est\nadapté : il exécute le code plusieurs fois et\natténue les fluctuations."
    }
  ]
}