{
  "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 cohérents",
          "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": "Des instructions dans l'ordre modifiant l'état",
          "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 fonctionnel** 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": "Évaluer des fonctions sans effet de bord",
          "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 orienté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": "Structurer le code autour d'objets",
          "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": "Cacher les détails derrière une interface",
          "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 la lisibilité, les tests et la maintenance",
          "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 multi-paradigmes",
          "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": "Strictement le seul 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 spéciale `include outils.py` en tête",
          "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` ou `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 vite, avec moins de bugs",
          "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 propre** ?",
      "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 (lire, calculer, afficher)",
          "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 pré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": [
        "organisation-modules",
        "couplage"
      ],
      "title": "Deux modules qui s'importent mutuellement",
      "statement": "Dans un projet, le module `calculs.py`\ncommence par `import affichage` pour\nmontrer ses résultats à l'écran, et le\nmodule `affichage.py` commence par\n`import calculs` pour obtenir les valeurs\nà afficher. Quel défaut de conception ce\nchoix révèle-t-il ?",
      "options": [
        {
          "text": "Une dépendance circulaire, qui soude les deux modules l'un à l'autre",
          "correct": true,
          "feedback": "Bonne réponse : deux modules qui\ns'importent l'un l'autre forment en\nréalité un seul bloc. Impossible de\ntester `calculs` seul, de le réutiliser\ndans un autre projet ou de remplacer\n`affichage` sans toucher à `calculs`.\nC'est le couplage fort que la\nconception modulaire cherche à éviter :\n`calculs` doit se contenter de renvoyer\nses résultats, et c'est `affichage` (ou\nle programme principal) qui les montre.\n"
        },
        {
          "text": "Aucun défaut, car Python accepte les imports dans les deux sens",
          "correct": false,
          "feedback": "Erreur : Python charge bien les deux\nfichiers, mais la conception reste\ndéfectueuse, car aucun des deux modules\nne peut plus vivre séparément.\nL'exécution peut même échouer : si\n`affichage.py` écrit\n`from calculs import moyenne`, le\nprogramme s'arrête sur `ImportError`,\ncar `calculs` n'a pas fini de se charger\nau moment où `affichage` réclame sa\nfonction.\n"
        },
        {
          "text": "Un doublon de code, car chaque module recopie les fonctions de l'autre",
          "correct": false,
          "feedback": "Erreur : `import` ne recopie aucun\ncode. Chaque fonction n'existe qu'une\nfois, dans le fichier qui la définit.\nLe défaut n'est pas la duplication mais\nla dépendance mutuelle, qui attache les\ndeux modules l'un à l'autre.\n"
        },
        {
          "text": "Une perte de temps, car chaque appel de fonction recharge l'autre module",
          "correct": false,
          "feedback": "Erreur : un module n'est chargé qu'une\nseule fois, lors de son premier\n`import` ; les appels de fonctions qui\nsuivent ne rechargent rien. Le problème\nn'est pas la vitesse d'exécution mais\nl'organisation du programme, dans\nlaquelle les deux modules dépendent\nl'un de l'autre.\n"
        }
      ],
      "explanation": "Les dépendances entre modules doivent\nformer une hiérarchie sans cycle : un\nmodule de bas niveau (calculs, structures\nde données) ignore ceux qui l'utilisent\n(affichage, programme principal). Un module\nqui ne dépend que de la bibliothèque\nstandard est le plus simple à tester et à\nréutiliser."
    },
    {
      "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": "Même résultat pour mêmes paramètres, sans effet de bord",
          "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 responsabilité** » ?",
      "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 accomplit une seule tâche",
          "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": "Les points d'accès publics du module",
          "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": "Fonction d'ordre supérieur",
      "statement": "Laquelle de ces écritures utilise explicitement\nune **fonction d'ordre supérieur**, trait\ncaractéristique du style fonctionnel ?",
      "options": [
        {
          "text": "```python\ncarres = list(map(lambda x: x * x, liste))\n```\n",
          "correct": true,
          "feedback": "`map` est une fonction d'ordre\nsupérieur : elle reçoit une\nfonction en argument (ici une\n`lambda` anonyme) et l'applique\nà chaque élément de la\ncollection. C'est le trait\nle plus caractéristique du\nstyle fonctionnel.\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\nest d'esprit fonctionnel (pas\nde mutation explicite), mais\nelle n'appelle aucune fonction\nd'ordre supérieur : aucune\nfonction n'y est passée en\nargument à une autre fonction.\n"
        }
      ],
      "explanation": "Quelques outils fonctionnels en\nPython : `map`, `filter`,\n`reduce` et `lambda`. 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 standard** plutôt que de tout coder\nsoi-même ?",
      "options": [
        {
          "text": "Parce qu'aucun avantage réel ne distingue les deux approches",
          "correct": false,
          "feedback": "Les bibliothèques apportent au\ncontraire de nombreux\navantages : un code testé par\nde nombreux utilisateurs,\ndocumenté et maintenu.\n"
        },
        {
          "text": "Parce que Python impose d'utiliser ses bibliothèques standard",
          "correct": false,
          "feedback": "Aucune obligation n'est\nimposée : on peut tout\nréécrire soi-même. C'est une\nquestion de choix raisonné,\net non de contrainte.\n"
        },
        {
          "text": "Parce qu'elles rendent toujours le programme plus rapide",
          "correct": false,
          "feedback": "Une bibliothèque n'est pas\ntoujours plus rapide qu'un\ncode écrit à la main pour un\ncas précis. La performance\nn'est pas le critère\nprincipal pour préférer une\nbibliothèque, mais la qualité\net la fiabilité.\n"
        },
        {
          "text": "Parce que leur code est déjà testé, documenté et maintenu",
          "correct": true,
          "feedback": "Bonne réponse : ce code est de\nqualité, éprouvé par de très\nnombreux utilisateurs.\nLa 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, l'instance un objet concret",
          "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": "Cela dépend du problème à résoudre",
          "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": "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 en classes des cas qui ne le justifient pas",
          "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    def __init__(x, y):\n        self.x = x\n        self.y = y\n```\n",
          "correct": false,
          "feedback": "Il manque le paramètre `self`\nen tête de `__init__`. À la\ncréation de l'objet, Python\ntransmet l'instance comme\npremier argument : l'appel\n`Point(3, 4)` fournit donc\ntrois arguments à une méthode\nqui n'en attend que deux, d'où\nune erreur `TypeError`.\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": "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 données et méthodes, en masquant l'interne",
          "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 de commentaire (`#nom`)",
          "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 le nom d'un underscore (`_nom`)",
          "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."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "synthese",
        "paradigme",
        "identification"
      ],
      "title": "Reconnaître un paradigme",
      "statement": "Considérez le fragment Python suivant :\n\n`print(sum(map(lambda x: x ** 2, range(10))))`\n\nQuel **paradigme** de programmation ce style illustre-t-il\nprincipalement ?",
      "options": [
        {
          "text": "La programmation fonctionnelle (`map`, `lambda`)",
          "correct": true,
          "feedback": "Bonne réponse : la programmation fonctionnelle se\nreconnaît à la composition de fonctions, à\nl'absence de variables intermédiaires modifiées\nen place, et à l'utilisation de fonctions comme\nparamètres (ici, la `lambda` passée à `map`).\nPython supporte ce style tout en restant\nprincipalement impératif.\n"
        },
        {
          "text": "La programmation orientée objet, car on utilise des méthodes implicites",
          "correct": false,
          "feedback": "Erreur : aucune classe ni méthode n'est définie\nexplicitement ici. Le code ne s'articule pas autour\nd'objets et de messages, mais autour de la\ncomposition de fonctions.\n"
        },
        {
          "text": "La programmation impérative, car on imprime un résultat à l'écran",
          "correct": false,
          "feedback": "Erreur : la présence de `print` ne suffit pas à\ncaractériser le style. Le cœur du calcul est\nfonctionnel ; `print` est juste l'instruction\nd'affichage finale.\n"
        },
        {
          "text": "La programmation logique, car le code déclare un ensemble de contraintes",
          "correct": false,
          "feedback": "Erreur : la programmation logique (Prolog par\nexemple) repose sur des règles et des prédicats,\npas sur des compositions de fonctions. Le fragment\nprésenté ici n'est pas déclaratif au sens logique.\n"
        }
      ],
      "explanation": "Un même langage peut accueillir plusieurs paradigmes.\nPython est multiparadigme : on peut écrire ce calcul\nde manière impérative\n(`total = 0; for x in range(10): total += x ** 2`),\norientée objet (en passant par une classe), ou\nfonctionnelle (comme ici). Choisir le style adapté à\nchaque problème fait partie de la culture du\nprogrammeur."
    },
    {
      "id": "q28",
      "difficulty": 3,
      "skills": [
        "synthese",
        "couplage",
        "cohesion",
        "approfondissement"
      ],
      "title": "Cohésion et couplage",
      "statement": "Une recommandation classique de la conception modulaire\nénonce qu'il faut viser une **forte cohésion** à\nl'intérieur de chaque module et un **faible couplage**\nentre les modules. Que signifie cette consigne ?",
      "options": [
        {
          "text": "Chaque module groupe des fonctions liées et expose une interface minimale",
          "correct": true,
          "feedback": "Bonne réponse : la cohésion mesure à quel point les\néléments d'un module concourent à un même objectif ;\nle couplage mesure le degré de dépendance entre\nmodules distincts. Un programme bien modularisé\nmaximise la cohésion (modules thématiquement\nunifiés) et minimise le couplage (interfaces\nstables et restreintes).\n"
        },
        {
          "text": "Chaque module fait beaucoup de choses très différentes et dépend fortement des autres modules pour s'exécuter",
          "correct": false,
          "feedback": "Erreur : c'est exactement l'inverse, et c'est ce\nqu'on cherche à éviter. Un module qui fait trop de\nchoses et dépend de tout devient impossible à\nmaintenir et à réutiliser.\n"
        },
        {
          "text": "La cohésion et le couplage sont deux noms d'une même propriété et doivent toujours être identiques",
          "correct": false,
          "feedback": "Erreur : ce sont deux propriétés distinctes,\nmesurées à des niveaux différents (interne au\nmodule pour la cohésion, entre modules pour le\ncouplage).\n"
        },
        {
          "text": "Il faut copier le code d'un module dans les autres pour qu'ils soient totalement indépendants",
          "correct": false,
          "feedback": "Erreur : la duplication de code est un anti-patron\n(au sens de mauvaise pratique). Elle multiplie les\nendroits à corriger en cas de bug et nuit à la\nmaintenabilité.\n"
        }
      ],
      "explanation": "Ces deux critères se traduisent concrètement dans le\ncode : une fonction ou une classe ne devrait avoir\nqu'une responsabilité claire ; un module ne devrait\npas avoir besoin de connaître les détails internes des\nautres. Les langages modernes facilitent cela par les\nespaces de noms, les modules, les classes et les\ninterfaces."
    }
  ]
}