Geldautomat als Python-Skript

Einleitung
PEP81 konformes Python-Skript zur Wechselgeldberechnung eines Geldbetrages in Euro und Cent
Basisskript
Zusätzliche Funktionen:
- Restangabe, um die Berechnung leichter zu kontrollieren.
- Umwandlung von Kommas in Punkte, um Verwirrung in der Eingabe zu vermeiden
- Es wird auf zwei Nachkommastellen gerundet, um Floating-Point-Fehler zu vermeiden2
# Eingabe des Betrags in Euro
betrag_in_euro = input("Bitte Betrag in Euro eingeben: ")
# Umwandlung des Betrags in Cent. Außerdem werden Kommas in Punkte umgewandelt
betrag_in_cent = float(betrag_in_euro.replace(',', '.')) * 100
# Verfügbare Scheine und Münzen in Euro
einheiten_in_euro = [100, 50, 20, 10, 5, 2, 1, 0.50, 0.20, 0.10, 0.05, 0.02, 0.01]
# Variable festlegen, damit wir sie später verwenden können
ergebnis = {}
# Berechnung des Wechselgelds für jede Einheit
for einheit in einheiten_in_euro:
anzahl = 0
# Solange der Betrag in Cent größer oder gleich der aktuellen Einheit ist
while betrag_in_cent >= int(einheit * 100):
# ...wird der Wert der aktuellen Einheit von betrag_in_cent abgezogen
betrag_in_cent -= int(einheit * 100)
anzahl += 1
# Solange ein oder mehr Scheine benötigt werden, fügen wir sie zum Ergebnis hinzu
if anzahl > 0:
# Hinzufügen der Einheit und des Euro-Betrags zum Ergebnis
ergebnis[f"{anzahl} x {einheit} Euro"] = (anzahl * einheit, betrag_in_cent / 100)
# Ausgabe des Wechselgelds
output = "Wechselgeld:\n"
for einheit, (euro_betrag, rest) in ergebnis.items():
output += f"{einheit}: {euro_betrag}€ (Rest: {rest:.2f}€)\n"
# Ausgabe des gesamten Ergebnisses
print(output)
Soviel dazu. Jetzt wollen wir das Ganze aber etwas professioneller darstellen. Das Ganze ist in mehrere Schritte aufgeteilt.
1. Aufteilen
Das Basisskript ist jetzt in zwei Teile eingeteilt: Die eigentliche Funktion als berechne_wechselgeld()
und alles andere
Funktion berechne_wechselgeld(betrag_in_euro)
def berechne_wechselgeld(betrag_in_euro):
# Umwandlung des Betrags in Cent
betrag_in_cent = float(betrag_in_euro.replace(',', '.')) * 100
# Verfügbare Scheine und Münzen in Euro
einheiten_in_euro = [100, 50, 20, 10, 5, 2, 1, 0.50, 0.20, 0.10, 0.05, 0.02, 0.01]
# Variable festlegen, damit wir sie später verwenden können
ergebnis = {}
# Berechnung des Wechselgelds für jede Einheit
for einheit in einheiten_in_euro:
anzahl = 0
# Solange der Betrag in Cent größer oder gleich der aktuellen Einheit ist
while betrag_in_cent >= int(einheit * 100):
# ...wird der Wert der aktuellen Einheit von betrag_in_cent abgezogen
betrag_in_cent -= int(einheit * 100)
anzahl += 1
# Solange ein oder mehr Scheine benötigt werden, fügen wir sie zum Ergebnis hinzu
if anzahl > 0:
# Hinzufügen der Einheit und des Euro-Betrags zum Ergebnis
ergebnis[f"{anzahl} x {einheit} Euro"] = (anzahl * einheit, betrag_in_cent / 100)
return ergebnis
alles andere
Durch den Python-"Trick" if __name__ == '__main__':
wird der in dieser if-Schleife deklarierte Code nur ausgeführt, wenn das Programm direkt vom User ausgeführt wird. Das hat den Vorteil, dass wenn das Skript als Modul eines anderen Programms genutzt wird, der dortige Code ignoriert wird. Das machen wir uns zunutze und schreiben hier unseren UX-Code hinein.
if __name__ == '__main__':
# Eingabe des Betrags in Euro
betrag_in_euro = input("Bitte Betrag in Euro eingeben: ")
if not betrag_in_euro:
print("Keine Eingabe, das Programm wird beendet.")
exit()
# Basisprogramm ausführen
wechselgeld_ergebnis = berechne_wechselgeld(betrag_in_euro)
# Ausgabe des Wechselgelds
output = "Wechselgeld:\n"
for einheit, (euro_betrag, rest) in wechselgeld_ergebnis.items():
output += f"{einheit}: {euro_betrag}€ (Rest: {rest:.2f}€)\n"
# Ausgabe des gesamten Ergebnisses
print(output)
2. Fehlerverarbeitung
Im Falle eines ValueErrors, wie wenn wir Buchstaben anstatt von Zahlen in die Eingabe setzen, crasht das Skript. Mit dem try
-except
-Befehl können wir das Programm versuchen lassen, den Code auszuführen, wenn aber ein Error zustande kommt, den Fehler als Variable speichern.
if __name__ == '__main__':
# Eingabe des Betrags in Euro
betrag_in_euro = input("Bitte Betrag in Euro eingeben: ")
if not betrag_in_euro:
print("Keine Eingabe, das Programm wird beendet.")
exit()
try:
# Basisprogramm ausführen
wechselgeld_ergebnis = berechne_wechselgeld(betrag_in_euro)
# Ausgabe des Wechselgelds
output = "Wechselgeld:\n"
for einheit, (euro_betrag, rest) in wechselgeld_ergebnis.items():
output += f"{einheit}: {euro_betrag}€ (Rest: {rest:.2f}€)\n"
# Ausgabe des gesamten Ergebnisses
print(output)
except ValueError as Error:
print(f"Fehler: {Error}")
Der Output wäre jetzt anstatt
Bitte Betrag in Euro eingeben: abc
Traceback (most recent call last):
File "$PATH/main.py", line 21, in <module>
betrag_in_cent = float(betrag_in_euro.replace(',', '.')) * 100
ValueError: could not convert string to float: 'abc'
so:
Bitte Betrag in Euro eingeben: abc
Fehler: could not convert string to float: 'abc'
und der Exitcode beträgt immer noch 0
3. Module
Durch die Nutzung der vorhin besprochenen Funktion, können wir das Programm nun als Modul in einer anderen Datei verwenden. Das hat folgende Vorteile:
- Einfache Integration in andere Programme durch "leise" Ausgabe
- Bessere Lesbarkeit durch Auslagerung
Ein Beispiel hierfür wäre:
import Geldautomat
from time import sleep
# Einfache Abfrage des Nutzers:
eingabe = str(input("Welcher Betrag soll berechnet werden? "))
print(Geldautomat.berechne_wechselgeld(eingabe))
sleep(2)
# Repetitive Nutzung:
for i in range(100):
i = str(i)
print(Geldautomat.berechne_wechselgeld(i))
und der Output ist:
Welcher Betrag soll berechnet werden? 456
{'4 x 100 Euro': (400, 56.0), '1 x 50 Euro': (50, 6.0), '1 x 5 Euro': (5, 1.0), '1 x 1 Euro': (1, 0.0)}
{}
{'1 x 1 Euro': (1, 0.0)}
{'1 x 2 Euro': (2, 0.0)}
{'1 x 2 Euro': (2, 1.0), '1 x 1 Euro': (1, 0.0)}
{'2 x 2 Euro': (4, 0.0)}
{'1 x 5 Euro': (5, 0.0)}
[...]
4. Unittest
Das Testing-Framework Unittest
ist seit Python Version 2.1 in den Standardbibliotheken integriert.
Durch die leichte Definition von Testabläufen können wir Programme mit Leichtigkeit auf ihre Robustheit testen.
Das Programm wird hier auf folgende Funktionen getestet:
- Genauigkeit
- Negative Zahlen
- Große Zahlen
- Kleine Zahlen
- Kommas als Trennzeichen
- Mehrere Nachkommastellen
import unittest
import Geldautomat # Importiere die Datei Geldautomat mit ihren Funktionen als Datei
class TestWechselgeldBerechnung(unittest.TestCase):
def test_genauigkeit(self):
# Testen, ob der Betrag genau in die kleinsten Einheiten aufgeteilt wird
betrag = "1623.45" # Beispielbetrag als Zeichenkette
ergebnis = Geldautomat.berechne_wechselgeld(betrag)
self.assertTrue(sum(euro for euro, _ in ergebnis.values()) == float(betrag), "Die Gesamtsumme stimmt nicht überein.")
def test_negative_zahlen(self):
# Testen, wie das Skript auf negative Zahlen reagiert
betrag = "-50" # Beispielbetrag als Zeichenkette
ergebnis = Geldautomat.berechne_wechselgeld(betrag)
self.assertEqual(len(ergebnis), 0, "Negative Beträge sollten keine Ergebnisse liefern.")
def test_grosse_zahlen(self):
# Testen mit einem sehr hohen Betrag
betrag = "334598764.34" # Beispielbetrag als Zeichenkette
ergebnis = Geldautomat.berechne_wechselgeld(betrag)
self.assertTrue(len(ergebnis) > 0, "Große Zahlen sollten verarbeitet werden können.")
def test_kleine_zahlen(self):
# Testen mit einem sehr kleinen Betrag
betrag = "0.03" # Beispielbetrag als Zeichenkette
ergebnis = Geldautomat.berechne_wechselgeld(betrag)
self.assertTrue(len(ergebnis) > 0, "Cents sollten verarbeitet werden können.")
def test_komma_als_trennzeichen(self):
# Testen, ob ein Betrag mit einem Komma anstatt eines Punktes als Trennzeichen funktioniert
betrag = "1234,56" # Beispielbetrag mit Punkt als Zeichenkette
lösung = betrag.replace(',', '.') # Da Python mit Punkten als Trennungszeichen rechnet, müssen wir das ganze hier modifizieren
ergebnis = Geldautomat.berechne_wechselgeld(betrag)
self.assertTrue(sum(euro for euro, _ in ergebnis.values()) == float(lösung), "Punkt als Trennzeichen sollte funktionieren.")
def test_viele_nachkommastellen(self):
# Testen, ob Eingaben mit vielen Nachkommastellen korrekt bearbeitet werden
betrag = "456.56565567" # Beispielbetrag mit vielen Nachkommastellen als Zeichenkette
ergebnis = Geldautomat.berechne_wechselgeld(betrag)
self.assertTrue(sum(euro for euro, _ in ergebnis.values()) == 456.56, "Eingaben mit vielen Nachkommastellen sollten bis zur zweiten Nachkommastelle abgeschnitten werden.")
if __name__ == '__main__':
unittest.main(verbosity=2)
Und der Output:
test_genauigkeit (__main__.TestWechselgeldBerechnung) ... ok
test_grosse_zahlen (__main__.TestWechselgeldBerechnung) ... ok
test_kleine_zahlen (__main__.TestWechselgeldBerechnung) ... ok
test_komma_als_trennzeichen (__main__.TestWechselgeldBerechnung) ... ok
test_negative_zahlen (__main__.TestWechselgeldBerechnung) ... ok
test_viele_nachkommastellen (__main__.TestWechselgeldBerechnung) ... ok
----------------------------------------------------------------------
Ran 6 tests in 0.528s
OK
Falls nun einer der Fälle einen Error ausspuckt, weil er nicht korrekt durchläuft sähe das so aus:
test_genauigkeit (__main__.TestWechselgeldBerechnung) ... ok
test_grosse_zahlen (__main__.TestWechselgeldBerechnung) ... ERROR
test_kleine_zahlen (__main__.TestWechselgeldBerechnung) ... ok
test_komma_als_trennzeichen (__main__.TestWechselgeldBerechnung) ... ok
test_negative_zahlen (__main__.TestWechselgeldBerechnung) ... ok
test_viele_nachkommastellen (__main__.TestWechselgeldBerechnung) ... ok
======================================================================
ERROR: test_grosse_zahlen (__main__.TestWechselgeldBerechnung)
----------------------------------------------------------------------
Traceback (most recent call last):
File "$PATH/Unittest.py", line 34, in test_grosse_zahlen
ergebnis = Geldautomat.berechne_wechselgeld(betrag)
File "$PATH/Geldautomat.py", line 23, in berechne_wechselgeld
betrag_in_cent = float(betrag_in_euro.replace(',', '.')) * 100
ValueError: could not convert string to float: '334e598764.34'
----------------------------------------------------------------------
Ran 6 tests in 0.001s
FAILED (errors=1)
Info:
Folgende Dateien sind hier zum Download möglich:
- Main.py
- Erweitert - Geldautomat.py
- Erweitert - Unittest.py
- Erweitert - Modul-Beispiel.py
- ZIP Ordner
- Code über Github