Een programma over bestanden verdelen · oefening
Importstijlen en code tijdens het importeren
Een import doet meer dan een naam beschikbaar maken. Die voert het bestand uit, van boven naar beneden, één keer.
Die zin verklaart het meeste verrassende gedrag dat beginners met imports tegenkomen. Het is daarom de moeite waard het te zien gebeuren.
Wat import echt doet
Stel dat noisy.py dit bevat:
print("noisy.py is being imported")
GREETING = "hello"
def shout():
return GREETING.upper()
Importeren drukt iets af. Niet wanneer shout wordt aangeroepen, maar op het moment van de import.
import noisy # prints "noisy.py is being imported"
print(noisy.shout()) # prints "HELLO"
Elke regel op het hoogste niveau wordt uitgevoerd. print drukt af, GREETING = "hello" wijst toe en de def-instructie maakt een functie. Een functie definiëren gebeurt zelf op het moment van importeren; de functie aanroepen niet.
Eén keer per uitvoering
Binnen één uitvoering van het programma voert twee keer dezelfde module importeren de code normaal één keer uit:
import noisy # prints
import noisy # prints nothing
Python houdt bij wat al is geïmporteerd en geeft hetzelfde moduleobject terug. Daarom kunnen drie bestanden allemaal import models doen zonder dat er drie kopieën van Question bestaan. Daardoor is ook main.Question is models.Question waar.
Tijdens één uitvoering van het programma importeren zowel main.py als report.py normaal models. Hoe vaak wordt models.py uitgevoerd?
Waarom dit uitmaakt voor hoe je een module schrijft
Een module die bij het importeren werk doet, dringt dat werk op aan iedereen die die importeert, gewenst of niet.
# scoring.py, written carelessly
def percentage(score, total):
...
# a quick demo the author left in
print(percentage(4, 10))
Importeer scoring vanaf elke willekeurige plek en er wordt iets afgedrukt. Je testsuite drukt iets af. Een webverzoek drukt iets af. Je kunt percentage niet stil gebruiken, omdat de demo deel is van het bestand in plaats van van een programma.
De oplossing is de controle die je sinds hoofdstuk 12 van Python-basis I ziet:
def percentage(score, total):
...
if __name__ == "__main__":
print(percentage(4, 10))
__name__ is een variabele die Python in elke module instelt. Als een bestand rechtstreeks wordt uitgevoerd, is die "__main__". Als het bestand wordt geïmporteerd, is die de eigen modulenaam, "scoring". Het bewaakte blok wordt dus uitgevoerd als je het bestand uitvoert en blijft stil als iemand het importeert.
Dat is het hele mechanisme en het maakt een bestand tot twee dingen tegelijk: een module die anderen kunnen gebruiken en een programma dat je kunt uitvoeren.
De regel die hieruit volgt
Het hoogste niveau van een module hoort dingen te definiëren, niet dingen te doen.
Definities, constanten en imports op het hoogste niveau zijn prima, omdat ze bepalen wat de module is. Afdrukken, om invoer vragen, bestanden openen en functies aanroepen horen binnen een functie of achter de uitvoercontrole.
from models import * verdient hier ook aandacht, omdat het de andere importstijl is die beginners tegenkomen. Die haalt elke publieke naam uit een module naar het huidige bestand. Daardoor kun je bij het lezen niet zien waar een naam vandaan komt, kunnen twee modules elkaars namen ongemerkt overschrijven en kan het toevoegen van een naam aan een module een bestand stukmaken dat die naam nooit noemde. Gebruik het gerust in een tijdelijke REPL-sessie. Gebruik het niet in een bestand.
De oefening
Je schrijft scoring.py, de module die een score omzet in een percentage en een letterbeoordeling. Onderaan staat een demo die nu bij het importeren wordt uitgevoerd.
Opdracht
Maak scoring.py, het bestand dat bij de editor van deze les hoort.
Het moet twee functies definiëren en niets bevatten dat bij het importeren wordt uitgevoerd:
percentage(score, total)geeft de score terug als verhouding tussen0.0en1.0en geeft0.0terug wanneertotalnul is, in plaats van een exceptie op te gooien;grade_letter(fraction)geeft"A"terug bij0.9of hoger,"B"bij0.8,"C"bij0.7,"D"bij0.6en daaronder"F".
Behoud de demo onderaan, maar zet die achter if __name__ == "__main__":, zodat die draait als je het bestand uitvoert en stil blijft als een andere module het importeert.
De beoordelaar controleert zowel stil importeren als de demo bij rechtstreekse uitvoering. De knop Run van het project voert nog steeds main.py uit; scoring.py selecteren verandert dat niet. Submit controleert het scorebestand en de volgende les verbindt de functies met de draaiende applicatie.