{
  "chapter": {
    "id": "modularite-paradigmes",
    "level": "terminale",
    "theme": "Programmation",
    "title": "Modularité et paradigmes",
    "description": "Modularité d'un programme par séparation en modules\ncohérents, paradigmes de programmation (impératif,\nfonctionnel, objet), notion d'abstraction, intérêt\nde la décomposition pour la lisibilité et la\nréutilisation du code.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition-modularite"
      ],
      "title": "Modularité d'un programme",
      "statement": "Qu'appelle-t-on la **modularité** d'un\nprogramme ?",
      "options": [
        {
          "text": "Sa qualité esthétique",
          "correct": false,
          "feedback": "La modularité est une propriété\nstructurelle objective, et non un\njugement subjectif sur l'apparence\ndu code.\n"
        },
        {
          "text": "Sa taille mesurée en nombre de lignes",
          "correct": false,
          "feedback": "La taille en lignes ne reflète pas\nla modularité d'un programme. Un\nprogramme court peut être très peu\nmodulaire, et inversement.\n"
        },
        {
          "text": "Sa rapidité d'exécution",
          "correct": false,
          "feedback": "La rapidité d'exécution est une\npropriété distincte. Un programme\nmodulaire peut être rapide ou\nlent ; les deux qualités sont\nindépendantes.\n"
        },
        {
          "text": "Sa décomposition en modules (fonctions, classes, fichiers) cohérents et faiblement couplés entre eux",
          "correct": true,
          "feedback": "Un programme modulaire est plus\nlisible, plus testable et plus\nfacile à maintenir. La modularité\nest l'un des piliers du génie\nlogiciel.\n"
        }
      ],
      "explanation": "Deux notions clés sont à retenir : la\n**cohésion** (un module accomplit une\ntâche, et la fait bien) et le\n**couplage faible** (les modules\ndépendent peu les uns des autres). On\ncherche à maximiser la première et à\nminimiser le second."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "paradigme-imperatif"
      ],
      "title": "Paradigme impératif",
      "statement": "Qu'est-ce que le **paradigme impératif**\nen programmation ?",
      "options": [
        {
          "text": "Une simple variante du paradigme fonctionnel",
          "correct": false,
          "feedback": "Les paradigmes impératif et\nfonctionnel sont au contraire\ntrès différents : ils s'opposent\nnotamment sur la place accordée à\nla mutation des variables.\n"
        },
        {
          "text": "Un style de programmation interdit par la loi",
          "correct": false,
          "feedback": "Cette interprétation littérale du\nmot « impératif » est sans rapport\navec son sens informatique. Aucun\nparadigme de programmation n'est\ninterdit par une loi.\n"
        },
        {
          "text": "Un paradigme inventé en $2020$",
          "correct": false,
          "feedback": "Le paradigme impératif est très\nancien : il est associé aux\ndébuts mêmes de la programmation\ndans les années $1950$.\n"
        },
        {
          "text": "Un style de programmation où l'on donne des instructions à exécuter dans l'ordre, en modifiant l'état du programme par l'intermédiaire de variables",
          "correct": true,
          "feedback": "C'est le style de programmation le\nplus répandu, présent en C, en\nPascal, en Java, et partiellement\nen Python. Il consiste à décrire\n« comment faire » étape par\nétape, à la manière d'une\nrecette.\n"
        }
      ],
      "explanation": "Caractéristiques de ce paradigme :\nutilisation de variables modifiables,\nde boucles et d'instructions\nséquentielles. Cette approche est\nhistoriquement liée à l'architecture\nde von Neumann."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "paradigme-fonctionnel"
      ],
      "title": "Paradigme fonctionnel",
      "statement": "Qu'est-ce que le **paradigme\nfonctionnel** en programmation ?",
      "options": [
        {
          "text": "Un paradigme dédié à la gestion des erreurs",
          "correct": false,
          "feedback": "La gestion des erreurs n'est pas\nle sujet propre du paradigme\nfonctionnel. Celui-ci concerne\nplutôt la manière de structurer\nles calculs autour de fonctions\nsans effet de bord.\n"
        },
        {
          "text": "Un style fondé sur l'évaluation de fonctions sans effet de bord, l'immuabilité des données et la composition de fonctions",
          "correct": true,
          "feedback": "Les langages purement\nfonctionnels les plus connus sont\nHaskell, Lisp et OCaml. Python\npermet aussi d'adopter certains\naspects fonctionnels, par exemple\navec les expressions `lambda`,\nou les fonctions `map` et\n`filter`.\n"
        },
        {
          "text": "Tout simplement un programme qui fonctionne correctement",
          "correct": false,
          "feedback": "Cette interprétation littérale du\nmot « fonctionnel » est trompeuse.\nEn programmation, le paradigme\nfonctionnel renvoie à un style\nprécis, fondé sur l'évaluation de\nfonctions sans effet de bord.\n"
        },
        {
          "text": "Le même paradigme que l'impératif",
          "correct": false,
          "feedback": "Les deux paradigmes s'opposent\nsur des points fondamentaux,\nnotamment la mutation des\nvariables et la présence\nd'effets de bord.\n"
        }
      ],
      "explanation": "Avantages : un code plus prévisible\n(sans effets de bord cachés), un\nparallélisme facilité, et une plus\ngrande facilité de test. En\ncontrepartie, l'apprentissage peut\nparaître exigeant pour quelqu'un\nhabitué au style impératif."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "paradigme-objet"
      ],
      "title": "Paradigme objet",
      "statement": "Qu'est-ce que la **programmation\norientée objet** ?",
      "options": [
        {
          "text": "Le fait de coder en utilisant des objets physiques",
          "correct": false,
          "feedback": "Cette interprétation littérale est\nsans rapport avec le sens\ninformatique. Les objets en\nprogrammation orientée objet sont\ndes entités logicielles\nabstraites, pas des objets\nphysiques.\n"
        },
        {
          "text": "Un paradigme aujourd'hui obsolète",
          "correct": false,
          "feedback": "Au contraire, la programmation\norientée objet reste très utilisée\ndans le monde du logiciel, et\nnotamment dans les grands projets\nindustriels.\n"
        },
        {
          "text": "Une bibliothèque graphique",
          "correct": false,
          "feedback": "Le paradigme objet est une manière\nd'organiser le code en général,\net non une bibliothèque\nparticulière dédiée à\nl'affichage.\n"
        },
        {
          "text": "Un style structurant le code autour d'objets (instances de classes) qui combinent données (les attributs) et comportements (les méthodes)",
          "correct": true,
          "feedback": "Java, C++, Python et Ruby sont\ndes langages très utilisés en\nprogrammation orientée objet. Au\nprogramme officiel : la définition\nd'une classe, ses attributs, ses\nméthodes, et la création\nd'instances.\n"
        }
      ],
      "explanation": "La programmation orientée objet a été\nformalisée par Alan Kay dans le\nlangage Smalltalk en $1972$. Elle\nest particulièrement adaptée pour\nmodéliser des entités du monde réel\n(Voiture, Personne, Compte\nbancaire). Pour des problèmes\nsimples, elle peut toutefois\nalourdir inutilement le code."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "abstraction"
      ],
      "title": "Abstraction",
      "statement": "Qu'appelle-t-on **abstraction** en\nprogrammation ?",
      "options": [
        {
          "text": "Un paradigme de programmation particulier",
          "correct": false,
          "feedback": "L'abstraction est plus large\nqu'un paradigme : elle traverse\ntous les paradigmes (impératif,\nfonctionnel, objet) et fait\npartie du vocabulaire général de\nla programmation.\n"
        },
        {
          "text": "Une notion appartenant aux mathématiques",
          "correct": false,
          "feedback": "Le mot « abstraction » a un sens\nprécis en mathématiques, mais on\nparle ici de son sens\ninformatique : cacher les détails\nd'une implémentation derrière une\ninterface.\n"
        },
        {
          "text": "Le fait de cacher les détails d'une implémentation derrière une interface simple, de sorte que l'utilisateur d'une fonction n'ait pas à savoir comment elle est codée",
          "correct": true,
          "feedback": "Par exemple, on utilise\n`len(liste)` sans avoir besoin de\nconnaître l'implémentation de\ncette fonction. L'abstraction est\nle fondement même de la\nmodularité.\n"
        },
        {
          "text": "Une obligation imposée par la loi",
          "correct": false,
          "feedback": "L'abstraction est un principe de\nconception, sans rapport avec une\nquelconque obligation légale.\n"
        }
      ],
      "explanation": "Tous les langages offrent des\nmécanismes d'abstraction : les\nfonctions, les modules, les classes,\nles types abstraits. Un bon design\nlogiciel exploite ces mécanismes au\nmaximum pour produire un code\nlisible et maintenable."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "decoupage"
      ],
      "title": "Pourquoi découper un programme ?",
      "statement": "Pourquoi **découper un programme** en\nplusieurs fonctions ou modules ?",
      "options": [
        {
          "text": "Pour améliorer la lisibilité, la réutilisation, la testabilité et la maintenance du code",
          "correct": true,
          "feedback": "Un programme découpé en petites\nunités cohérentes est nettement\nplus facile à comprendre, à\ntester et à faire évoluer dans\nle temps.\n"
        },
        {
          "text": "Pour gagner quelques secondes lors de la compilation",
          "correct": false,
          "feedback": "Le gain en compilation, s'il\nexiste, est marginal et ne\nconstitue pas l'objectif\nprincipal du découpage en\nmodules.\n"
        },
        {
          "text": "Pour rendre le code plus complexe",
          "correct": false,
          "feedback": "C'est exactement l'inverse. Le\ndécoupage vise à **simplifier**\nle code en le structurant en\npetites unités intelligibles.\n"
        },
        {
          "text": "Aucune raison particulière ne le justifie",
          "correct": false,
          "feedback": "Le découpage répond au contraire à\nplusieurs objectifs concrets,\ncomme expliqué dans la bonne\nréponse.\n"
        }
      ],
      "explanation": "Une maxime classique du génie\nlogiciel résume bien cette idée :\n« une fonction, une responsabilité ».\nSi une fonction effectue plusieurs\ntâches distinctes, mieux vaut la\nscinder en autant de fonctions que\nde responsabilités identifiées."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "exemple-langages"
      ],
      "title": "Paradigmes du langage Python",
      "statement": "Quel paradigme **dominant** caractérise\nle langage Python ?",
      "options": [
        {
          "text": "Python est un langage multi-paradigmes : il permet de combiner les styles impératif, objet et fonctionnel selon les besoins",
          "correct": true,
          "feedback": "Python permet de mélanger\nlibrement les différents styles\nde programmation. C'est un\nlangage très flexible, qui laisse\nau programmeur le soin de\nchoisir l'approche la plus\nadaptée au problème traité.\n"
        },
        {
          "text": "Uniquement le paradigme objet",
          "correct": false,
          "feedback": "Python est certes très orienté\nobjet en interne (toute valeur\nest un objet), mais il accepte\nparfaitement d'autres styles de\nprogrammation, à commencer par\nle style impératif.\n"
        },
        {
          "text": "Uniquement le paradigme impératif",
          "correct": false,
          "feedback": "Python ne se limite pas au style\nimpératif : il offre des outils\nfonctionnels (`lambda`, `map`,\ncompréhensions) et un système\nobjet complet.\n"
        },
        {
          "text": "Uniquement le paradigme fonctionnel",
          "correct": false,
          "feedback": "Python n'est pas un langage\npurement fonctionnel. Il accepte\nla mutation des variables et les\neffets de bord, qui sont au\ncontraire évités en\nprogrammation purement\nfonctionnelle.\n"
        }
      ],
      "explanation": "Python est un langage multi-paradigmes\npar conception. De nombreux langages\nmodernes le sont également\n(JavaScript, Scala, Kotlin). Cette\nflexibilité permet de choisir le\nmeilleur outil pour chaque situation."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "import-modules"
      ],
      "title": "Importer un module",
      "statement": "En Python, comment **importe-t-on** des\nfonctions définies dans un module\n`outils.py` situé à côté du fichier\nprincipal ?",
      "options": [
        {
          "text": "Avec une directive `include outils.py`",
          "correct": false,
          "feedback": "La directive `include` existe en\nC ou en C++, mais pas en\nPython. Le mécanisme d'import\nest différent.\n"
        },
        {
          "text": "Avec `import outils` (puis `outils.fonction()`), ou avec `from outils import fonction`",
          "correct": true,
          "feedback": "Ce sont les deux syntaxes\nstandard. La première préserve\nl'espace de noms du module ; la\nseconde permet d'utiliser\ndirectement la fonction\nimportée, sans préfixe.\n"
        },
        {
          "text": "Avec l'instruction `use outils`",
          "correct": false,
          "feedback": "La syntaxe `use` n'existe pas en\nPython. C'est le mot-clé\n`import` qu'il faut utiliser.\n"
        },
        {
          "text": "Aucune syntaxe ne permet ce type d'import",
          "correct": false,
          "feedback": "Python propose au contraire un\nmécanisme d'import très complet,\ncomme indiqué dans la bonne\nréponse.\n"
        }
      ],
      "explanation": "La modularité en Python repose sur\nles modules, qui sont de simples\nfichiers d'extension `.py`, et sur\nles paquets, qui sont des dossiers\ncontenant un fichier `__init__.py`.\nCe mécanisme est simple à mettre en\nœuvre."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "reutilisation"
      ],
      "title": "Avantage de la réutilisation",
      "statement": "Quel est le **principal avantage** de la\nréutilisation de code déjà écrit ?",
      "options": [
        {
          "text": "Le code s'exécute plus rapidement",
          "correct": false,
          "feedback": "La rapidité d'exécution n'est\npas systématiquement améliorée\npar la réutilisation. Une\nbibliothèque générique peut\nparfois être plus lente qu'une\nimplémentation spécialisée.\n"
        },
        {
          "text": "On code plus rapidement, et avec moins de bugs, car le code réutilisé a déjà été testé",
          "correct": true,
          "feedback": "Plutôt que de tout réinventer, on\ns'appuie sur du code existant,\nqu'il s'agisse de la\nbibliothèque standard du langage\nou de bibliothèques externes\nreconnues. Cela permet un effet\nd'échelle : un même code testé\nsert à de nombreux programmes.\n"
        },
        {
          "text": "Aucun avantage particulier",
          "correct": false,
          "feedback": "La réutilisation apporte au\ncontraire des bénéfices\nimportants, comme expliqué dans\nla bonne réponse.\n"
        },
        {
          "text": "Le code occupe moins d'espace",
          "correct": false,
          "feedback": "La place occupée n'est pas le\ncritère principal. La\nréutilisation peut même\naugmenter la taille du programme\nsi la bibliothèque utilisée est\nvolumineuse.\n"
        }
      ],
      "explanation": "Une bonne pratique consiste à se\nfamiliariser avec la bibliothèque\nstandard de son langage avant\nd'écrire du code soi-même. Beaucoup\nde problèmes ont déjà une solution\néprouvée disponible."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "trace-fonction"
      ],
      "title": "Décomposition typique",
      "statement": "Pour calculer la moyenne d'une liste\nde notes lue dans un fichier au format\nCSV, quelle décomposition est la **plus\npropre** ?",
      "options": [
        {
          "text": "N'utiliser aucune fonction",
          "correct": false,
          "feedback": "Écrire un programme entier sans\naucune fonction est une mauvaise\npratique. Le découpage en\nfonctions reste essentiel à la\nlisibilité.\n"
        },
        {
          "text": "Tout placer dans une seule fonction",
          "correct": false,
          "feedback": "Une fonction qui mélange lecture,\ncalcul et affichage est plus\ndifficile à tester et à\nréutiliser. Mieux vaut séparer\nces responsabilités.\n"
        },
        {
          "text": "Trois fonctions distinctes, par exemple `lire_csv`, `calculer_moyenne` et `afficher_resultat`",
          "correct": true,
          "feedback": "Chaque fonction a alors une\nresponsabilité claire et unique.\nChacune peut être testée\nindépendamment et réutilisée\nailleurs dans le programme ou\ndans un autre projet.\n"
        },
        {
          "text": "Une seule fonction qui ne fait qu'un appel à `print`",
          "correct": false,
          "feedback": "Cette décomposition est trop\nminimaliste. Aucune des\nopérations principales (lecture,\ncalcul) n'est isolée dans sa\npropre fonction.\n"
        }
      ],
      "explanation": "Le principe de **séparation des\npréoccupations** consiste à isoler\nles entrées-sorties, le calcul, et\nla présentation des résultats. C'est\nune architecture classique, simple\net robuste."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "instanciation"
      ],
      "title": "Instancier une classe",
      "statement": "En Python, comment crée-t-on un objet\n(une **instance**) à partir de la\nclasse `Voiture` ?",
      "options": [
        {
          "text": "Avec `v = Voiture.instance()`",
          "correct": false,
          "feedback": "La méthode `instance()` n'est\npas la syntaxe standard\nd'instanciation en Python.\n"
        },
        {
          "text": "Avec la syntaxe `v = Voiture()`",
          "correct": true,
          "feedback": "On appelle la classe comme une\nfonction. Cet appel déclenche\nl'exécution de la méthode\n`__init__` et renvoie le nouvel\nobjet créé.\n"
        },
        {
          "text": "Avec `v = create(Voiture)`",
          "correct": false,
          "feedback": "Aucune fonction `create` n'est\nprésente dans la bibliothèque\nstandard de Python pour\ninstancier une classe.\n"
        },
        {
          "text": "Avec la syntaxe `v = new Voiture()`",
          "correct": false,
          "feedback": "La syntaxe `new` existe en Java\nou en JavaScript, mais elle\nn'est pas utilisée en Python.\n"
        }
      ],
      "explanation": "Pour créer une instance, on appelle\nsimplement la classe en lui passant\nles paramètres voulus, par exemple\n`v = Voiture(\"Renault\", 2020)`.\nPython crée alors l'objet et\nappelle automatiquement la méthode\nspéciale `__init__` pour\nl'initialiser."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "acces-attribut"
      ],
      "title": "Accéder à un attribut",
      "statement": "Soit l'instance\n`v = Voiture(\"Peugeot\", 2020)`. Comment\naccède-t-on à l'attribut `marque` de\ncet objet ?",
      "options": [
        {
          "text": "Avec la notation `v::marque`",
          "correct": false,
          "feedback": "Le double deux-points est\nutilisé en C++ (résolution de\nportée), mais pas en Python.\n"
        },
        {
          "text": "Avec la notation `v[marque]`",
          "correct": false,
          "feedback": "Les crochets servent à indexer\nune liste ou un dictionnaire,\net non à accéder à l'attribut\nd'un objet.\n"
        },
        {
          "text": "Avec la notation `v.marque`",
          "correct": true,
          "feedback": "La notation pointée permet\nd'accéder aussi bien aux\nattributs qu'aux méthodes d'un\nobjet. C'est la syntaxe\nstandard en Python.\n"
        },
        {
          "text": "Avec la notation `v->marque`",
          "correct": false,
          "feedback": "La flèche `->` est utilisée en\nC ou en C++, mais pas en\nPython.\n"
        }
      ],
      "explanation": "La notation `objet.attribut`\npermet de lire ou de modifier un\nattribut. La notation\n`objet.methode(...)` permet\nd'appeler une méthode définie sur\nl'objet."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "fonction-pure"
      ],
      "title": "Fonction pure",
      "statement": "Qu'est-ce qu'une **fonction pure**, au\nsens du paradigme fonctionnel ?",
      "options": [
        {
          "text": "Une fonction qui ne prend aucun paramètre",
          "correct": false,
          "feedback": "Le nombre de paramètres n'a\naucun rapport avec la pureté\nd'une fonction. Une fonction\npure peut prendre zéro, un ou\nplusieurs paramètres.\n"
        },
        {
          "text": "Une fonction écrite dans un langage mathématique",
          "correct": false,
          "feedback": "Cette description est trop\nvague. Une fonction pure se\ndéfinit précisément par\nl'absence d'effet de bord et\npar sa déterministicité, et non\npar son expression dans un\nlangage particulier.\n"
        },
        {
          "text": "Une fonction qui ne contient aucun bug",
          "correct": false,
          "feedback": "La pureté n'a aucun rapport\navec l'absence de bugs. Une\nfonction pure peut très bien\nêtre incorrecte ; inversement,\nune fonction avec effets de\nbord peut être correcte.\n"
        },
        {
          "text": "Une fonction qui, pour les mêmes paramètres, renvoie toujours la même valeur, sans aucun effet de bord (pas de modification d'une variable globale, pas d'entrée-sortie)",
          "correct": true,
          "feedback": "Ce type de fonction est\nprévisible, facilement\ntestable, et permet une\nparallélisation aisée. Un\nexemple : `def carre(x): return\nx * x`. À l'inverse, une\nfonction comme\n`def lire_fichier(f)` n'est\npas pure, car elle effectue\nune opération\nd'entrée-sortie.\n"
        }
      ],
      "explanation": "Un avantage important des\nfonctions pures est qu'elles\nautorisent la **mémoïsation**, qui\nconsiste à mettre en cache les\nrésultats déjà calculés. Ces\nfonctions facilitent aussi le\nraisonnement mathématique sur le\nprogramme."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "responsabilite-unique"
      ],
      "title": "Une fonction, une responsabilité",
      "statement": "Que recommande le principe\n« **une fonction, une\nresponsabilité** » ?",
      "options": [
        {
          "text": "Un programme ne doit posséder qu'une seule fonction",
          "correct": false,
          "feedback": "Cette interprétation est\nincorrecte. Un programme\ncontient en général de\nnombreuses fonctions, chacune\nayant sa responsabilité\npropre.\n"
        },
        {
          "text": "Chaque fichier doit contenir une seule fonction",
          "correct": false,
          "feedback": "Un module peut tout à fait\ncontenir plusieurs fonctions\nliées à un même thème. Ce\nprincipe ne porte pas sur\nl'organisation des fichiers.\n"
        },
        {
          "text": "Chaque fonction doit prendre exactement un seul paramètre",
          "correct": false,
          "feedback": "Le nombre de paramètres n'a\naucun rapport avec ce\nprincipe, qui concerne la\ntâche accomplie par la\nfonction.\n"
        },
        {
          "text": "Chaque fonction doit accomplir une seule tâche clairement définie",
          "correct": true,
          "feedback": "Ce principe est l'un des piliers\nde la modularité. Une fonction\nà responsabilité unique est\nplus fiable, plus lisible et\nplus facile à tester. Si l'on\nne peut pas décrire une\nfonction en une phrase courte,\nil vaut mieux la découper.\n"
        }
      ],
      "explanation": "Si une fonction `traiter` lit un\nfichier, effectue des calculs et\nécrit le résultat, mieux vaut la\nscinder en trois fonctions\ndistinctes, chacune dédiée à une\nseule responsabilité."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "interface"
      ],
      "title": "Interface d'un module",
      "statement": "Que désigne l'**interface** d'un\nmodule ou d'une fonction ?",
      "options": [
        {
          "text": "Uniquement la documentation du module",
          "correct": false,
          "feedback": "La documentation décrit\nl'interface, mais l'interface\nelle-même est un concept plus\nlarge : elle inclut aussi la\nsignature effective des\nfonctions et leur\ncomportement.\n"
        },
        {
          "text": "L'ensemble des points d'accès publics (signature, paramètres attendus, valeurs renvoyées, exceptions levées) que les utilisateurs peuvent consulter sans connaître l'implémentation",
          "correct": true,
          "feedback": "L'interface joue le rôle d'un\ncontrat public. Tant que ce\ncontrat est respecté, on peut\nmodifier l'implémentation sans\nimpacter les utilisateurs du\nmodule.\n"
        },
        {
          "text": "Le code source du module",
          "correct": false,
          "feedback": "Le code source représente\nl'**implémentation** du module,\nc'est-à-dire la manière dont il\nfonctionne en interne. C'est\nprécisément ce que l'interface\ncherche à masquer.\n"
        },
        {
          "text": "Sa couleur d'affichage dans l'éditeur",
          "correct": false,
          "feedback": "La couleur d'affichage relève\ndes préférences de l'éditeur,\net non de la définition d'une\ninterface en programmation.\n"
        }
      ],
      "explanation": "Une bonne pratique consiste à\n« programmer contre une interface,\npas contre une implémentation ».\nCela rend le code robuste face\naux changements internes des\nmodules dont il dépend."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "exemple-fonctionnel"
      ],
      "title": "Style fonctionnel",
      "statement": "Laquelle de ces écritures est la plus\n**proche du style fonctionnel** ?",
      "options": [
        {
          "text": "```python\ncarres = list(map(lambda x: x * x, liste))\n```\n",
          "correct": true,
          "feedback": "La fonction `map` applique une\nfonction à chaque élément\nd'une collection, et `lambda`\npermet de définir une fonction\nanonyme à la volée. C'est\nl'écriture la plus\ncaractéristique du style\nfonctionnel.\n"
        },
        {
          "text": "```python\ncarres = liste.map(square)\n```\n",
          "correct": false,
          "feedback": "Cette syntaxe n'est pas\nvalide en Python : les listes\nne possèdent pas de méthode\n`map`. La fonction `map` est\nune fonction native qui\ns'utilise différemment.\n"
        },
        {
          "text": "```python\ncarres = []\nfor x in liste:\n    carres.append(x * x)\n```\n",
          "correct": false,
          "feedback": "Cette écriture est typique du\nstyle impératif, avec une\nboucle et des modifications\nsuccessives d'une variable.\n"
        },
        {
          "text": "```python\ncarres = [x * x for x in liste]\n```\n",
          "correct": false,
          "feedback": "Cette compréhension de liste\nadopte un style proche du\nfonctionnel, mais l'écriture\nla plus emblématique du style\nfonctionnel utilise plutôt les\nfonctions `map` ou `filter`\ncombinées à des fonctions\nanonymes.\n"
        }
      ],
      "explanation": "Quelques outils fonctionnels en\nPython : `map`, `filter`,\n`reduce`, `lambda`, `zip` et\n`enumerate`. Ils sont\nidiomatiques mais doivent être\nutilisés avec discernement, sans\nsacrifier la lisibilité."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "reutilisation-bibli"
      ],
      "title": "Bibliothèques standard",
      "statement": "Pourquoi est-il préférable de\nprivilégier les **bibliothèques\nstandard** plutôt que de tout coder\nsoi-même ?",
      "options": [
        {
          "text": "Aucun avantage à utiliser des bibliothèques",
          "correct": false,
          "feedback": "Les bibliothèques apportent au\ncontraire de nombreux\navantages, comme détaillé dans\nla bonne réponse.\n"
        },
        {
          "text": "Parce que les bibliothèques standard sont obligatoires à utiliser",
          "correct": false,
          "feedback": "Aucune obligation n'est\nimposée. C'est une question\nde choix raisonné, et non de\ncontrainte.\n"
        },
        {
          "text": "Pour ralentir volontairement le programme",
          "correct": false,
          "feedback": "Aucune bibliothèque n'est\nconçue pour ralentir un\nprogramme. La performance\nn'est pas le critère\nprincipal pour préférer une\nbibliothèque, mais la qualité\net la fiabilité.\n"
        },
        {
          "text": "Le code des bibliothèques standard est de qualité, testé par de nombreux utilisateurs, documenté et maintenu",
          "correct": true,
          "feedback": "La probabilité de produire un\nmeilleur code seul, en\nquelques heures, qu'une\nbibliothèque standard\nmaintenue depuis des années\nest très faible. Sauf cas\nparticuliers (apprentissage,\ncontraintes spécifiques), la\nréutilisation est plus sage.\n"
        }
      ],
      "explanation": "Quelques bibliothèques standard\nutiles en Python : `math`,\n`random`, `csv`, `json`, `os`,\n`datetime`, `collections`,\n`itertools`. À cela s'ajoute un\nécosystème externe très riche,\navec des bibliothèques comme\nNumPy, pandas ou requests."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "methode-self"
      ],
      "title": "Le paramètre self",
      "statement": "Dans la définition d'une méthode\nPython, à quoi sert le premier\nparamètre, traditionnellement nommé\n`self` ?",
      "options": [
        {
          "text": "C'est un mot-clé réservé qu'il ne faut jamais modifier",
          "correct": false,
          "feedback": "Le nom `self` est une simple\nconvention, et non un mot-clé\nréservé du langage. On\npourrait techniquement\nl'appeler autrement, mais ce\nserait fortement déconseillé,\ncar cela briserait les\nhabitudes des autres\ndéveloppeurs.\n"
        },
        {
          "text": "Il représente la classe elle-même, pas l'instance",
          "correct": false,
          "feedback": "Le paramètre `self` représente\nprécisément l'**instance**\n(l'objet créé), et non la\nclasse. Pour désigner la\nclasse elle-même, on utilise\nplutôt `cls` dans les\nméthodes de classe.\n"
        },
        {
          "text": "Il est rempli automatiquement avec la valeur $0$",
          "correct": false,
          "feedback": "Le paramètre `self` reçoit\nl'objet courant lors de\nl'appel, et non une valeur\nnumérique fixe.\n"
        },
        {
          "text": "Il désigne l'instance sur laquelle la méthode est appelée",
          "correct": true,
          "feedback": "Le paramètre `self` permet à\nla méthode d'accéder aux\nattributs et aux autres\nméthodes de l'objet courant,\npar exemple `self.marque`. Il\nfait le lien entre la méthode\nappelée et l'objet sur lequel\nelle s'exécute.\n"
        }
      ],
      "explanation": "Lors d'un appel comme\n`v.afficher()`, Python transmet\nautomatiquement l'objet `v`\ncomme premier paramètre `self`.\nLa méthode peut alors lire ou\nmodifier les attributs de `v`\nvia la notation\n`self.attribut`."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "vocabulaire-poo"
      ],
      "title": "Classe et instance",
      "statement": "Quelle est la différence entre une\n**classe** et une **instance** ?",
      "options": [
        {
          "text": "Aucune différence",
          "correct": false,
          "feedback": "Les deux notions sont au\ncontraire bien distinctes,\ncomme expliqué dans la bonne\nréponse.\n"
        },
        {
          "text": "La notion d'instance est aujourd'hui obsolète",
          "correct": false,
          "feedback": "La notion d'instance reste\ncentrale en programmation\norientée objet : c'est l'objet\nconcret manipulé au cours de\nl'exécution.\n"
        },
        {
          "text": "La classe est le « plan » général (la description abstraite). L'instance est un objet concret créé à partir de ce plan",
          "correct": true,
          "feedback": "Une analogie utile : la\nclasse est comme un moule à\ngâteaux, et les instances\nsont les gâteaux eux-mêmes.\nOn écrit `class Voiture:`\npour définir une classe et\n`ma_voiture = Voiture()` pour\nen créer une instance.\n"
        },
        {
          "text": "La classe s'exécute plus rapidement que l'instance",
          "correct": false,
          "feedback": "La rapidité d'exécution n'est\npas la différence pertinente\nentre classe et instance. Ce\nsont deux notions distinctes,\npas deux variantes d'un même\nconcept.\n"
        }
      ],
      "explanation": "Une classe peut avoir aucune,\nune, ou de très nombreuses\ninstances. Chaque instance\npossède son propre état (ses\npropres valeurs d'attributs),\nmais les méthodes restent\ndéfinies une seule fois, dans\nla classe."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "comparaison-paradigmes"
      ],
      "title": "Choisir un paradigme",
      "statement": "Quel paradigme de programmation\nfaut-il privilégier ?",
      "options": [
        {
          "text": "Le paradigme objet, dans tous les cas",
          "correct": false,
          "feedback": "Cette position est trop\ndogmatique. Aucun paradigme\nn'est universellement\nsupérieur aux autres ; le\nchoix dépend du problème à\nrésoudre.\n"
        },
        {
          "text": "Le paradigme fonctionnel, dans tous les cas",
          "correct": false,
          "feedback": "Cette position est elle\naussi trop dogmatique. Le\nparadigme fonctionnel\nexcelle pour les\ntransformations de données,\nmais il n'est pas toujours\nle plus naturel pour tous\nles problèmes.\n"
        },
        {
          "text": "N'importe quel paradigme convient toujours",
          "correct": false,
          "feedback": "Il existe souvent un choix\nplus pertinent qu'un autre,\nen fonction de la nature du\nproblème à résoudre.\n"
        },
        {
          "text": "Le choix dépend du problème : le paradigme objet pour modéliser des entités complexes ; le paradigme fonctionnel pour des transformations de données ; le paradigme impératif pour des algorithmes simples et directs",
          "correct": true,
          "feedback": "Aucun paradigme n'est\nuniversellement meilleur. Un\nbon programmeur sait choisir\nl'outil adapté au contexte.\nBeaucoup de programmes\nmodernes mélangent\nd'ailleurs plusieurs\nparadigmes au sein d'un même\nprojet.\n"
        }
      ],
      "explanation": "La tendance moderne est aux\nlangages multi-paradigmes, qui\npermettent de mélanger les\nmeilleurs aspects de chaque\napproche selon le contexte."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "couplage"
      ],
      "title": "Couplage entre modules",
      "statement": "Qu'appelle-t-on le **couplage** entre\nmodules ?",
      "options": [
        {
          "text": "Le nombre de fonctions définies dans chaque module",
          "correct": false,
          "feedback": "Le nombre de fonctions est\nune mesure de taille, pas une\nmesure de dépendance entre\nmodules.\n"
        },
        {
          "text": "Une union solennelle entre deux modules",
          "correct": false,
          "feedback": "Cette interprétation\nlittérale est sans rapport\navec le sens technique du mot\n« couplage » en\nprogrammation.\n"
        },
        {
          "text": "Une opération mathématique sur les modules",
          "correct": false,
          "feedback": "Le mot « couplage » est ici\nutilisé dans un sens\ninformatique précis, sans\nrapport avec une opération\nmathématique.\n"
        },
        {
          "text": "Le degré de dépendance entre modules. On vise un couplage faible, c'est-à-dire des modules peu liés, modifiables indépendamment les uns des autres",
          "correct": true,
          "feedback": "Un couplage faible permet de\nremplacer ou de modifier un\nmodule sans avoir à toucher\naux autres. Un couplage fort,\nau contraire, impose de\nmodifier plusieurs modules\nsimultanément à chaque\nchangement.\n"
        }
      ],
      "explanation": "Une astuce utile : si l'on ne\npeut pas modifier un module sans\nen regarder cinq autres, le\ncouplage est trop fort. Il faut\nalors refactoriser, par exemple\nen introduisant des interfaces\nclaires et en isolant les\ndépendances."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "piege-poo"
      ],
      "title": "Piège classique de la programmation orientée objet",
      "statement": "Quel est un **piège classique** de la\nprogrammation orientée objet ?",
      "options": [
        {
          "text": "Coder de manière trop simple",
          "correct": false,
          "feedback": "Le piège évoqué ici est\nprécisément l'inverse :\nc'est l'excès de complexité\nqui pose problème, pas la\nsimplicité.\n"
        },
        {
          "text": "La programmation orientée objet ne présente aucun piège",
          "correct": false,
          "feedback": "Comme tout paradigme, la\nprogrammation orientée objet\na ses pièges, dont l'excès\nde modélisation est l'un des\nplus classiques.\n"
        },
        {
          "text": "Modéliser sous forme de classes des situations qui ne le justifient pas, ce qui complique inutilement le code",
          "correct": true,
          "feedback": "Pour calculer une simple\nsomme de notes, il n'est pas\nnécessaire de créer une\nclasse `CalculateurDeMoyenne`.\nUne fonction suffit\nlargement. La programmation\norientée objet a un coût en\ncomplexité qu'il faut savoir\npeser.\n"
        },
        {
          "text": "Utiliser des fonctions classiques",
          "correct": false,
          "feedback": "Utiliser des fonctions n'est\npas un piège ; c'est au\ncontraire souvent la bonne\nsolution face à des\nproblèmes simples.\n"
        }
      ],
      "explanation": "Une bonne maxime : choisir\nl'outil adapté au problème. Une\nclasse pour seulement deux\nfonctions s'apparente à un\nempilement inutile. À partir\nd'une méthode importante avec\nun état persistant, la classe\ndevient en revanche pertinente."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "definition-classe"
      ],
      "title": "Définir une classe",
      "statement": "Quel squelette permet de définir une\nclasse `Point` avec deux attributs `x`\net `y` initialisés à la création ?",
      "options": [
        {
          "text": "```python\ndef Point(x, y):\n    self.x = x\n    self.y = y\n```\n",
          "correct": false,
          "feedback": "Cette définition utilise\n`def`, qui définit une\nfonction, et non une classe.\nIl manque le mot-clé\n`class`.\n"
        },
        {
          "text": "```python\nclass Point(x, y):\n    return x, y\n```\n",
          "correct": false,
          "feedback": "Les parenthèses qui suivent\nle nom d'une classe ne\nservent pas à déclarer ses\nattributs. Une définition\nde classe ne renvoie pas\nnon plus de valeur.\n"
        },
        {
          "text": "```python\nclass Point:\n    x, y\n```\n",
          "correct": false,
          "feedback": "Cette syntaxe n'est pas\nvalide en Python. De plus,\naucun attribut ne serait\ninitialisé.\n"
        },
        {
          "text": "```python\nclass Point:\n    def __init__(self, x, y):\n        self.x = x\n        self.y = y\n```\n",
          "correct": true,
          "feedback": "La méthode `__init__` est\nappelée automatiquement à la\ncréation de l'objet. Elle\nreçoit `self`, puis les\nvaleurs initiales, et les\nstocke en tant qu'attributs\nde l'instance.\n"
        }
      ],
      "explanation": "Le constructeur s'écrit\ntoujours sous la forme\n`def __init__(self, …):` et\nstocke les paramètres reçus\ndans `self`. Pour instancier la\nclasse, on écrit par exemple\n`p = Point(3, 4)` ; ensuite,\n`p.x` vaut $3$ et `p.y` vaut\n$4$."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "paradigme-declaratif"
      ],
      "title": "Paradigme déclaratif",
      "statement": "En quoi le paradigme **déclaratif**\nse distingue-t-il du paradigme\nimpératif ?",
      "options": [
        {
          "text": "Aucune différence n'existe entre les deux paradigmes",
          "correct": false,
          "feedback": "Les deux paradigmes\ndiffèrent au contraire de\nmanière fondamentale dans\nla manière de formuler le\nproblème.\n"
        },
        {
          "text": "Le paradigme déclaratif n'existe pas",
          "correct": false,
          "feedback": "Le paradigme déclaratif\nexiste bel et bien et est\nutilisé dans de très\nnombreux langages\nspécialisés.\n"
        },
        {
          "text": "Le paradigme déclaratif dit ce que l'on veut, sans préciser comment l'obtenir (SQL, HTML, expressions régulières) ; le paradigme impératif, à l'inverse, décrit comment faire étape par étape",
          "correct": true,
          "feedback": "Le paradigme fonctionnel et\nle paradigme logique\n(Prolog) sont deux variantes\ndéclaratives. Le langage\nSQL est typiquement\ndéclaratif : on demande\n« les noms des étudiants\nayant obtenu plus de $10$ »\nsans préciser comment\neffectuer la recherche.\n"
        },
        {
          "text": "Le paradigme déclaratif est plus rapide à exécuter",
          "correct": false,
          "feedback": "La rapidité n'est pas le\ncritère pertinent. La\ndistinction porte sur la\nmanière d'exprimer le\nproblème.\n"
        }
      ],
      "explanation": "La tendance moderne est de\nmonter en abstraction, en\ndéclarant ce que l'on veut\nplutôt qu'en décrivant\nl'implémentation. Par exemple,\nla bibliothèque React (style\ndéclaratif) s'oppose au style\nimpératif d'une bibliothèque\ncomme jQuery."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "statement": "Parmi les affirmations suivantes\nsur la modularité et les\nparadigmes, laquelle est\n**fausse** ?",
      "options": [
        {
          "text": "Python est un langage purement fonctionnel",
          "correct": true,
          "feedback": "Cette affirmation est fausse\n(donc c'est la bonne\nréponse). Python est un\nlangage **multi-paradigmes**.\nIl prend en charge des\naspects fonctionnels\n(`lambda`, `map`,\ncompréhensions), mais il\nautorise aussi la mutation\ndes variables et les effets\nde bord, ce qui n'est pas\npossible dans un langage\npurement fonctionnel.\n"
        },
        {
          "text": "La modularité améliore la maintenabilité du code",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte. La modularité est\nprécisément un facteur clé\nde maintenabilité.\n"
        },
        {
          "text": "Une fonction pure n'a pas d'effet de bord",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte. L'absence d'effet\nde bord est précisément ce\nqui définit une fonction\npure.\n"
        },
        {
          "text": "Le paradigme objet structure le code autour d'objets combinant données et comportements",
          "correct": false,
          "feedback": "Cette affirmation est\ncorrecte. C'est même la\ndéfinition du paradigme\nobjet.\n"
        }
      ],
      "explanation": "Les langages purement\nfonctionnels les plus connus\nsont Haskell et Idris. Les\nlangages multi-paradigmes\nincluent Python, JavaScript,\nScala et OCaml."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "encapsulation"
      ],
      "title": "Encapsulation",
      "statement": "Que désigne le principe\nd'**encapsulation** en programmation\norientée objet ?",
      "options": [
        {
          "text": "Réutiliser les classes\nd'un autre programme\n",
          "correct": false,
          "feedback": "Erreur : la réutilisation est\nun autre concept (lié à la\nmodularité). L'encapsulation\nconcerne le **masquage** des\ndétails internes derrière une\ninterface publique.\n"
        },
        {
          "text": "Mettre tout le code d'une\napplication dans un seul fichier\n",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse de la\nmodularité. L'encapsulation va\nprécisément dans le sens\nopposé : on isole les\nresponsabilités dans des objets\n(ou modules) distincts.\n"
        },
        {
          "text": "Compresser un fichier avant de\nl'envoyer\n",
          "correct": false,
          "feedback": "Erreur : la compression de\ndonnées est sans rapport avec\nle concept d'encapsulation. Le\nterme « encapsulation » a un\nsens informatique précis,\ndifférent.\n"
        },
        {
          "text": "Regrouper dans un même objet les\ndonnées (attributs) et les\ntraitements (méthodes) qui\ns'appliquent à ces données, en\nmasquant à l'extérieur les\ndétails d'implémentation\n",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'un des\npiliers de la programmation\norientée objet. L'utilisateur\nd'un objet manipule l'interface\npublique (les méthodes) sans\ndépendre du fonctionnement\ninterne (les attributs). On\npeut alors modifier\nl'implémentation sans casser\nle code utilisateur, tant que\nl'interface reste stable.\n"
        }
      ],
      "explanation": "L'encapsulation favorise le\ncouplage faible : un objet expose\nuniquement ce dont les autres ont\nbesoin (son interface publique) et\ncache le reste. C'est la base du\nprincipe « programmer contre une\ninterface, pas contre une\nimplémentation »."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "conventions-python"
      ],
      "title": "Convention de visibilité en Python",
      "statement": "En Python, comment indique-t-on\nconventionnellement qu'un attribut\nou une méthode est destiné à un\nusage **interne** (et ne fait pas\npartie de l'interface publique) ?",
      "options": [
        {
          "text": "En écrivant le nom en majuscules\n",
          "correct": false,
          "feedback": "Erreur : la convention des\nmajuscules désigne plutôt les\n**constantes** en Python\n(`PI`, `MAX_SIZE`). Elle n'a\naucun rapport avec la\nvisibilité.\n"
        },
        {
          "text": "En préfixant le nom par un dièse\n(`#nom`)\n",
          "correct": false,
          "feedback": "Erreur : le dièse n'a pas ce\nrôle en Python. Il introduit\nun commentaire de fin de\nligne, ce qui n'a aucun\nrapport avec la visibilité\ndes attributs.\n"
        },
        {
          "text": "En préfixant son nom par un\nunique caractère de soulignement\n(par exemple `_etat_interne`)\n",
          "correct": true,
          "feedback": "Bonne réponse : c'est la\nconvention pythonique. Cet\nunique préfixe ne change rien\nau niveau du langage (l'accès\nreste possible techniquement),\nmais il indique clairement aux\nautres développeurs que\nl'attribut ne fait pas partie\nde l'API publique. C'est une\nforme d'encapsulation par\nconvention. Le double\nsoulignement (`__nom`)\ndéclenche en plus le\n*name mangling* (renommage\ninterne).\n"
        },
        {
          "text": "Avec le mot-clé `private`\n",
          "correct": false,
          "feedback": "Erreur : le mot-clé `private`\nexiste en Java, en C++, en\nC#, mais pas en Python. Le\nlangage ne permet pas de\nrendre un attribut\nstrictement privé : tout\nrepose sur des conventions.\n"
        }
      ],
      "explanation": "Python privilégie la philosophie\n« We are all consenting adults »\n(« nous sommes tous des adultes\nconsentants ») : le langage ne\nbloque rien, mais la convention\nde l'unique soulignement signale\nqu'on ne doit pas accéder à\nl'attribut depuis l'extérieur.\nC'est moins strict qu'en Java ou\nen C++, mais plus souple."
    }
  ]
}