Een programma over bestanden verdelen · oefening
Modules en naamruimten
Voor de modules die je in dit hoofdstuk schrijft, is een module een Pythonbestand met samenhangende definities. Python biedt ook modules in andere vormen, maar een gewoon .py-bestand is alles wat dit project nodig heeft.
Wat een module nuttig maakt is dat de namen erbij horen. Twee bestanden kunnen allebei Question definiëren zonder te botsen en code die er een gebruikt moet aangeven welke.
Een zelfgeschreven bestand importeren
Hoofdstuk 8 leende een hulpmiddel uit de standaardbibliotheek:
from dataclasses import dataclass
Dezelfde instructie bereikt een eigen bestand. Als je project models.py bevat, is models een module die je kunt importeren:
import models
question = models.Question("Which keyword starts a function?", "def", 2)
Als dit project vanuit main.py is gestart, vindt Python models.py in de projectmap, voert het één keer uit en geeft je een object met alles wat dat bestand heeft gedefinieerd. models.Question leest uit dat object.
Let op wat er niet staat: geen pad, geen .py en geen aanhalingstekens. Je importeert de naam van de module en die naam is de bestandsnaam zonder extensie.
Naamruimten concreet
Stel je nu een tweede bestand voor, survey.py, dat voor eigen doeleinden ook Question definieert. In één bestand zouden die twee om de naam strijden. Als modules doen ze dat niet:
import models
import survey
quiz_question = models.Question("Which keyword starts a function?", "def", 2)
survey_question = survey.Question("How difficult was this?")
models.Question en survey.Question zijn verschillende klassen en de code zegt elke keer welke die bedoelt. Dat is een naamruimte: een plek waar namen staan, zodat dezelfde naam op twee plekken twee dingen blijft.
Dit is hetzelfde idee als self.answer dat een attribuut op één object benoemt in plaats van een losse variabele, maar dan een niveau hoger.
Je project heeft een bestand models.py. Welke regel importeert de module onder de naam models, zodat je models.Question(...) kunt schrijven?
Opsplitsen is verplaatsen, niet herschrijven
De stap die je gaat zetten is de eenvoudigste vorm van refactoring: tekst verlaat het ene bestand en komt ongewijzigd in het andere terecht.
Toch is bewust kiezen nuttig, omdat de interessante vraag niet is hoe je een klasse verplaatst, maar welke klassen bij elkaar horen. Question, Quiz en Attempt horen in dezelfde module omdat ze hetzelfde soort dingen zijn: de woordenschat waarover het programma gaat en de regels die erbij horen. Die groepering heet het domein en les 6 geeft het een metgezel.
Wat achterblijft is alles wat geen woordenschat is: een poging omzetten in tekstregels, een specifieke quiz samenstellen en het programma uitvoeren. Dat zijn taken die met het domein worden gedaan, geen onderdelen ervan.
De oefening
Kopieer Question, Quiz en Attempt vanuit main.py naar het gegeven bestand models.py, precies zoals ze zijn.
Deze les opent models.py. Selecteer main.py in het bestandenpaneel om de oorspronkelijke klassen te lezen en ga daarna terug naar models.py om ze te kopiëren.
Laat main.py voor deze stap ongewijzigd, zodat Run nog steeds het werkende origineel uitvoert. De volgende les maakt de verplaatsing af door de dubbele klassen te verwijderen en de nieuwe module te importeren. Submit controleert hier alleen models.py.
Opdracht
Kopieer Question, Quiz en Attempt naar de gegeven models.py.
Kopieer alle drie de klassen precies zoals ze in main.py staan. Dit is verplaatsen, niet herschrijven: gedrag mag niet veranderen en niets hoeft te worden hernoemd.
Die drie zijn de woordenschat van het programma en horen dus bij elkaar. models.py moet ze bevatten en verder niets: laat report_lines, build_quiz en main waar ze staan.
Laat de oorspronkelijke klassen voorlopig in main.py staan. Run gebruikt nog steeds dat oorspronkelijke programma; Submit controleert models.py. De volgende les verwijdert de duplicaten en importeert de klassen in plaats daarvan, waarmee de verplaatsing af is.