Ich würde Dir empfehlen, das ganze in einer Funktion zu kapseln, nennen wir sie ``calc_change`` (oder zur Not ``berechne_Wechselgeld``, wenn es denn Deutsch sein muss). Du speicherst diese Funktion einfach in einer Datei Namens `` change.py``. Dann sieht Dein Modul ``change`` nun so aus:
Code: Alles auswählen
COINS = (1, 2, 5, 10, 20, 50, 100, 200)
def calc_change(money, coins=COINS):
pass
Da ich immer mit fixen Münzen rechnen will, definiere ich einfach ein Modul-globales Tupel mit den vorgegebenen Münzen. Dieses übergebe ich der Funktion als Default-Parameter, damit ich den Wert nicht bei jedem Aufruf angeben muss. Ich kann sie aber auch mit einem anderen Portfolio aus Münzen aufrufen kann - wenn ich das denn einmal will.
Nun schreibe ich einfach mal ein Stück Code, welches testet, ob beim Aufruf der Funktion mit 57 Cent wirklich 1x 50, 1x 5 und 1x 2 Cents zurückgegeben werden:
Code: Alles auswählen
def test_calc_change_with_57_cents_returns_50_5_2_cent_coins():
result = calc_change(57)
assert result == [50, 5, 2]
Nun ergänze ich obige Funktion noch so, dass ich das als ein lauffähiges Testprogramm ausführen kann:
Code: Alles auswählen
#!/usr/bin/env python
from calc_change import calc_change
def test_calc_change_with_57_cents_returns_50_5_2_cent_coins():
result = calc_change(57)
assert result == [50, 5, 2]
def main():
try:
test_calc_change_with_57_cents_returns_50_5_2_cent_coins()
print("Test erfolgreich!")
except AssertionError:
print("Failed!")
if __name__ == "__main__":
main()
Das ``assert`` Statement in Python bewirkt, dass eine ``AssertException`` geworfen wird, sofern die Bedingung nach der Anweisung ``False`` ergibt. Dies mache ich mir zu Nutze und fange die Exception beim Aufruf der Testfunktion einfach ab. Tritt eine Exception auf, weiß ich, dass der Test fehlgeschlagen ist, wenn nicht, weiß ich, dass er erfolgreich war.
Wenn ich nun mein Testprogramm ausführe, so muss ich "Failed!" lesen, da ich die Funktion noch nicht richtig implementiert habe. Nun kann ich mich an die Implementierung der Funktion machen und immer mal wieder prüfen, ob ich sie denn vollständig implementiert habe. Sobald ich die Erfolgsmeldung sehe, weiß ich, dass die Funktion so funktioniert, wie ich mir das vorgestellt (und durch einen Test abgesichert!) habe.
Der Vorteil davon ist, dass ich nicht jdes Mal *manuell* irgend welche Zahlen eingeben muss, um zu testen, ob mein Algorithmus funktioniert
Dies nennt man "Test Driven Development" (TDD).
Ein Glück bietet Python bereits fertige Module dafür an, so dass ich den ganzen Overhead für die Auswertung nicht selber schreiben muss. Ich habe für das Problem mal mit
pytest einige Test geschrieben, die auch einen Randfall abdecken, sowie die Konstellation, dass eine Münze mehrfach in der Lösung vorkommt:
Code: Alles auswählen
#!/usr/bin/env python
from change import calc_change
def test_calc_change_with_57_cents_returns_50_5_2_cent_coins():
result = calc_change(57)
assert result == [50, 5, 2]
def test_calc_change_with_0_cents_returns_empty_list():
result = calc_change(0)
assert result == []
def test_calc_change_with_44_cents_returns_20_20_2_2_cent_coins():
result = calc_change(44)
assert result == [20, 20, 2, 2]
Wenn ich das ohne Implementierung der ``calc_change``-Funktion aufrufe erhalte ich folgende Ausgabe:
Code: Alles auswählen
1 nelson@vitory ~/Source/Python/Snipptes/Forum/change % py.test unit_tests.py
======================================= test session starts ========================================
platform linux -- Python 3.4.0 -- py-1.4.20 -- pytest-2.5.2
plugins: cov
collected 3 items
unit_tests.py FFF
============================================= FAILURES =============================================
_____________________ test_calc_change_with_57_cents_returns_50_5_2_cent_coins _____________________
def test_calc_change_with_57_cents_returns_50_5_2_cent_coins():
result = calc_change(57)
> assert result == [50, 5, 2]
E assert None == [50, 5, 2]
unit_tests.py:8: AssertionError
________________________ test_calc_change_with_0_cents_returns_empty_tuple _________________________
def test_calc_change_with_0_cents_returns_empty_tuple():
result = calc_change(0)
> assert result == []
E assert None == []
unit_tests.py:13: AssertionError
___________________ test_calc_change_with_44_cents_returns_20_20_2_2_cent_coins ____________________
def test_calc_change_with_44_cents_returns_20_20_2_2_cent_coins():
result = calc_change(44)
> assert result == [20, 20, 2, 2]
E assert None == [20, 20, 2, 2]
unit_tests.py:18: AssertionError
===================================== 3 failed in 0.02 seconds =====================================
In den drei Zeilen, die mit "E" beginnen sehe ich, was ``result`` tatsächlich war. In meinem Falle immer ``None``, da die Funktion ja nichts (also ``None``) zurückliefert, egal, was ich als Parameter übergebe.
Nachdem ich die Funktion implementiert habe, sehe ich folgende Ausgabe:
Code: Alles auswählen
1 nelson@vitory ~/Source/Python/Snipptes/Forum/change % py.test unit_tests.py :(
======================================= test session starts ========================================
platform linux -- Python 3.4.0 -- py-1.4.20 -- pytest-2.5.2
plugins: cov
collected 3 items
unit_tests.py ...
===================================== 3 passed in 0.01 seconds =====================================
Alle drei Tests sind erfolgreich durchgelaufen
Man kann sich nun noch mehr Tests überlegen, etwa für den Fall, dass man ein leeres Münztupel übergibt oder dass man einen negativen Betrag übergibt usw. Damit kann man seine Funktion immer robuster machen und sicher stellen, dass man an alles gedacht hat.
Ein großer Vorteil von solchen automatisierten Tests (in diesem Falle sogar tatsächliche Unit-Tests) ist auch, dass ich quasi nach jeder Zeile Code, die ich schreibe, den Test erneut aufrufen kann und somit *ohne* manuelle Eingaben *alle* bis dato erdachten Szenarien durchtesten kann.
Zum Schluss kannst Du das Modul ``change`` noch zu einem lauffähigen Programm erweitern, indem Du folgende Zeilen ergänzt:
Code: Alles auswählen
def main():
money = int(int(input("Geldbetrag in Euro: ")) * 100)
change = calc_change(money)
print("Der Betrag setzt sich zusammen aus: ", str(change))
if __name__ == "__main__":
main()
Die Ausgabe kann man natürlich noch aufhübschen
Du kannst natürlich auch im nachhinein Tests für Deine Funktion schreiben. Ich würde Dir das wirklich nahelegen.