Zo kun je programmeren in Python - Deel 10 

Door: koen-vervloesem | 05 april 2021 06:02

How To

Tot nu toe leerden we je programmeren, maar we leerden je geen programma schrijven. Het verschil? Programmeren doe je voor jezelf; een programma schrijven voor iemand anders. Je wilt dat een gebruiker eenvoudig een programma kan oproepen met opties, een mooi resultaat te zien krijgt en ook hulp kan opvragen als hij niet weet hoe hij jouw programma moet gebruiken. Dat alles zien we in deze laatste les van Python.

In deze tiende les ronden we stilaan af. Tot nu toe leerde je allerlei aspecten van Python kennen en die brengen we in deze les allemaal samen. Op het einde van deze les heb je een programma dat je op de opdrachtregel kunt uitvoeren, dat argumenten afhandelt, foutmeldingen geeft en tekst in kleur toont.

Argumenten op de opdrachtregel

In les 3 al leerde je om invoer van de gebruiker te vragen met de functie input. En hoewel we ons programma daar in Thonny uitvoerden, vertelden we daar ook al dat je het programma in Windows, Linux of macOS ook op de opdrachtregel kunt uitvoeren met:

python3 naamvanprogramma.py

Maar van programma’s op de opdrachtregel verwachten we dat je er ook argumenten aan kunt doorgeven, zoals DIR /s in Windows of ls -R in Linux en macOS. Hoe zorgen we dat onze eigen programma’s dat ook kunnen? Daar heeft Python gelukkig een oplossing voor met de module argparse in de standaardbibliotheek. In deze les schrijven we een programma dat de module palindroom uit les 6 gebruikt om van woorden te controleren of ze een palindroom zijn, en dat programma gaan we enkele argumenten geven.

Argumenten toevoegen

Als eerste willen we dat het programma ons vertelt of een woord dat we als argument aan het programma doorgeven een palindroom is. Dat ziet er zo uit:

from argparse import ArgumentParser

from palindroom import is_palindroom

parser = ArgumentParser(description='Bepaal of een woord een palindroom is.')

parser.add_argument("-w", "--woord", required=True, help="Het woord waarvan je wilt controleren of het een palindroom is")

args = parser.parse_args()

resultaat = is_palindroom(args.woord)

if resultaat:

print("Het woord '{}' is een palindroom.".format(args.woord))

else:

print("Het woord '{}' is geen palindroom.".format(args.woord))

Je ziet dat met argparse werken niet zo moeilijk is. Je maakt eerst een ArgumentParser-object aan met de beschrijving van je programma. Daarna voeg je een argument toe (in ons geval in een korte versie -w en een lange versie --woord) en lees je met parser.parse_args() de argumenten uit die de gebruiker op de opdrachtregel ingegeven heeft. De inhoud van het argument --woord vind je dan in de variabele args.woord.

Heel wat functionaliteit

Voer het programma nu eens uit:

$ python3 palindroomchecker.py -w parterretrap

Het woord 'parterretrap' is een palindroom.

$ python3 palindroomchecker.py -w parterretrak

Het woord 'parterretrak' is geen palindroom.

Merk op: we krijgen heel wat functionaliteit ‘gratis’ omdat argparse die voor ons uitvoert. Zo krijg je een foutmelding als je na -w geen woord opgeeft of als je het programma zonder het argument -w uitvoert (omdat we het argument toegevoegd hebben met required=True). En we krijgen ook het argument --help er gratis bij. Als je dit opgeeft, toont het programma de beschrijving van je programma en van alle argumenten die je gedefinieerd hebt:

$ python3 palindroomchecker.py -w

usage: palindroomchecker.py [-h] -w WOORD

palindroomchecker.py: error: argument -w/--woord: expected one argument

$ python3 palindroomchecker.py

usage: palindroomchecker.py [-h] -w WOORD

palindroomchecker.py: error: the following arguments are required: -w/--woord

$ python3 palindroomchecker.py --help

usage: palindroomchecker.py [-h] -w WOORD

Bepaal of een woord een palindroom is.

optional arguments:

-h, --help

-w WOORD, --woord WOORD

Het woord waarvan je wilt controleren of het een

palindroom is

Argumenten die elkaar uitsluiten

We willen het programma nu uitbreiden zodat je de palindroomtest ook één voor één kunt uitvoeren op alle regels in een tekstbestand. Daarvoor voegen we het argument -b toe. Maar het heeft geen zin om zowel -w als -b tegelijk op te geven, dus we moeten kunnen aangeven dat beide argumenten elkaar uitsluiten. Ook dat kan eenvoudig met argparse:

parser = ArgumentParser(description='Bepaal of een woord een palindroom is.')

bron = parser.add_mutually_exclusive_group(required=True)

bron.add_argument("-w", "--woord", help="Het woord waarvan je wilt controleren of het een palindroom is")

bron.add_argument("-b", "--bestand", help="Het bestand met woorden waarvan je wilt controleren of ze een palindroom zijn")

args = parser.parse_args()

Met parser.add_mutually_exclusive_group maken we een groep argumenten aan die elkaar uitsluiten. Met required=True van die methode zegen we dat de gebruiker exact een van de argumenten uit de groep moet gebruiken. Daarna voegen we onze argumenten aan die groep toe in plaats van aan het object parser.

Als we nu de controle of een woord een palindroom is en het tonen van het resultaat afsplitsen in een afzonderlijke functie toon_resultaat (dat laten we als oefening over aan jou), kunnen we beide gevallen van invoer (een woord op de opdrachtregel of woorden in een bestand waarvan je de naam op de opdrachtregel doorgeeft) eenvoudig afhandelen:

if args.woord:

toon_resultaat(args.woord)

else:

with open(args.bestand, 'rt') as bestand:

for regel in bestand:

toon_resultaat(regel.strip())

Probeer dit maar eens uit. Je zult zien dat je programma een foutmelding geeft als je geen of juist allebei de argumenten opgeeft.

In kleur

Je kunt ook kleuren toevoegen aan de uitvoer van je programma. Daarvoor gebruiken we het pakket colorama, dat je eerst nog met pip3 (zie de vorige les) dient te installeren omdat het niet tot de standaardbibliotheek van Python behoort. We importeren enkele functies uit de module en initialiseren daarna colorama:

from colorama import init, Fore, Back, Style

init()

Daarna kun je spelen met de voorgrondkleur, achtergrondkleur en helderheid van de tekst:

print(Fore.RED + 'Rode tekst...')

print(Back.GREEN + '... met een groene achtergrond.')

print(Style.RESET_ALL + Fore.RED + Style.DIM + 'Dof...')

print(Fore.GREEN + Style.BRIGHT + '... of juist helder.')

print(Style.RESET_ALL)

print('En nu weer normaal!')

Op het einde van je programma voeg je het best altijd deze regel toe:

deinit()

Die reset de uitvoer, zodat de opdrachtregel na het uitvoeren van je programma niet plots helemaal in het rood is omdat je de voorgrondkleur bent vergeten terug te zetten!

Optionele argumenten

Die kleuren gaan we in onze palindroomchecker alleen gebruiken als je een optioneel argument --kleur aan je programma doorgeeft. Zo’n optioneel argument voegen we als volgt toe:

parser.add_argument("-k", "--kleur", action="store_true", help="Toon het gevraagde woord in kleur")

In ons programma kunnen we dan op de aanwezigheid van het optionele argument testen met if args.kleur:. Het if-else-blok uit ons originele programma vervangen we dan door de volgende geneste structuur:

if resultaat:

if args.kleur:

palindroom = Fore.GREEN + Style.BRIGHT + args.woord + Style.RESET_ALL

else:

palindroom = args.woord

print("Het woord '{}' is een palindroom.".format(palindroom))

else:

if args.kleur:

geen_palindroom = Fore.RED + Style.DIM + args.woord + Style.RESET_ALL

else:

geen_palindroom = args.woord

print("Het woord '{}' is geen palindroom.".format(geen_palindroom))

Fouten afhandelen

Als je nu in ons programma dat het argument -b voor een bestand ondersteunt een bestandsnaam doorgeeft die niet ondersteund is, breekt ons programma af met de volgende foutmelding en dan de bestandsnaam:

FileNotFoundError: [Errno 2] No such file or directory:

Bovendien krijg je ook de regel code te zien waarin het fout loopt. Voor een gebruiker die niets van Python kent, is dat nogal intimiderend. We gaan dus met een try-catch-blok die exception afhandelen (zie les 5) en een gebruiksvriendelijkere foutmelding tonen.

Voor we dat doen, moet je nog iets weten. Windows, Linux en macOS kennen drie ‘virtuele bestanden’ die belangrijk zijn op de opdrachtregel: stdin (de invoer van het toetsenbord), stdout (de normale uitvoer van het programma) en stderr (de foutenuitvoer van het programma). Standaard tonen we met print de uitvoer op stdout, maar met file=sys.stderr leiden we de foutenuitvoer om naar stderr. Dat ziet er dan als volgt uit (vergeet niet de regel import sys in het begin van je programma):

if args.woord:

toon_resultaat(args.woord)

else:

try:

with open(args.bestand, 'rt') as bestand:

for regel in bestand:

toon_resultaat(regel.strip())

except FileNotFoundError as fout:

print('Bestand {} niet gevonden'.format(fout.filename), file=sys.stderr)

We proberen hier dus het bestand te openen. Als dat niet lukt, vangen we een exception FileNotFoundError af. Merk op: met except FileNotFoundError as fout: kennen we de exception toe aan de variabele fout, zodat we nuttige informatie uit dit object kunnen opvragen. In dit geval is dat de bestandsnaam waarover het gaat (fout.filename). Die tonen we in de foutmelding aan de gebruiker, zodat die kan nakijken waarom het bestand niet bestaat.

De meldingen die we op stderr tonen, verschijnen overigens gewoon in de opdrachtregel zoals andere tekst die je met print toont. Maar het verschil is belangrijk. Zo kunnen andere programma’s bijvoorbeeld je foutmeldingen op stderr er uitfilteren.

Samenvatting

In deze les heb je geleerd hoe je een volledig Python-script ontwikkelt dat je op de opdrachtregel kunt uitvoeren. Je kunt argumenten afhandelen en foutmeldingen geven en je weet hoe je van kleur gebruik kunt maken in je programma. Daarmee sluiten we deze lessenreeks af. Je bent nu klaar om je eigen Python-programma’s te schrijven!

Opdracht

Voeg een optioneel argument ‘stil’ aan je programma toe dat alleen uitvoer geeft als het gevraagde woord een palindroom is, zodat je snel palindromen in grote bestanden kunt vinden.

Uitwerking

Het optionele argument voeg je eenvoudig als volgt toe:

parser.add_argument("-s", "--stil", action="store_true", help="Toon het gevraagde woord als het een palindroom is; toon niets als het geen palindroom is.")

Daarna definiëren we de functie toon_resultaat als volgt:

def toon_resultaat(woord):

resultaat = is_palindroom(woord)

if resultaat:

if args.stil:

print(woord)

else:

print("Het woord '{}' is een palindroom.".format(woord))

else:

if not args.stil:

print("Het woord '{}' is geen palindroom.".format(woord))

In deze functie controleren we achtereenvolgens of het woord en palindroom is en of de gebruiker het optionele argument ‘stil’ heeft ingevoerd. Als we een palindroom hebben en het programma werkt stil, tonen we alleen het palindroom. Als we een palindroom hebben en het programma werkt niet stil, vertellen we in een hele zin dat het om een palindroom gaat. Als we geen palindroom hebben en het programma werkt stil, tonen we niets. En als we geen palindroom hebben en het programma werkt niet stil, vertellen we in een hele zin dat het niet om een palindroom gaat.

Cheatsheet

Stderr: de foutenuitvoer van een programma

Stdout: de normale uitvoer van een programma

0 Reactie(s) op: Zo kun je programmeren in Python - Deel 10 

  • Om te reageren moet je ingelogd zijn. Nog geen account? Registreer je dan en praat mee!
  • Er zijn nog geen reacties op dit artikel.

Wanneer je een reactie plaatst ga je akoord
met onze voorwaarden voor reacties.