@snowflake: `sys.exit()` kann und sollte man sich sparen. Die Anweisung ist unnötig weil das Programm an der Stelle sowieso ganz regulär ended wenn man es einfach bis zum Ende weiterlaufen lässt. Die Funktion macht, wenn überhaupt, nur Sinn wenn man nicht mit dem Exitstatus 0 beenden will. Ansonsten ist das oft ein „code smell“, dass man sich nicht genügend Gedanken über den Programmfluss gemacht hat und eine Abkürzung nehmen musste.
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm sollte also nicht direkt unter dem ``if __name__ …`` stehen, sondern in einer Funktion. Denn sonst ist die Variable `geburtsdatum` auf Modulebene bekannt, was nicht sein sollte.
Das Argument von `alterberechnen()` (oder besser `alter_berechnen()`) ist kryptisch abgekürzt. Das sollte `geburtsdatum` heissen, damit der Leser nicht raten muss was das `g` in `gdatum` bedeuten soll.
Die Funktion sollte auch gleich das Datum bekommen und nicht eine Zeichenkette umwandeln *und* das Alter in Jahren berechnen. Der Kommentar beim ``try`` ist irreführend, denn da wird ja nirgends geprüft ob ein Geburtsdatum vorliegt.
Statt `split()` würde ich die `strptime()`-Methode verwenden.
Ganz wichtig: Keine ”nackten” ``except:``-Blöcke, ausser wenn da ein (nacktes) ``raise`` drin steht oder zumindest die komplette Ausnahme inklusive Traceback irgendwo gespeichert/protokolliert wird. Das sind sonst schwarze Löcher für Fehler oder Probleme die man dann nur sehr schwer findet. Sinnvolle Fehlerbehandlung behandelt nur die Ausnahmen die man an der Stelle erwartet. Wenn eine unerwartete Ausnahme auftritt, kann man sich ja schliesslich gar nicht sicher sein, das die Behandlung die dort im ``except``-Block steht, auch wirklich Sinn macht, oder ob man die Ausnahme nicht anders behandeln müsste, und in der Regel auch an anderer Stelle. In vorliegenden Programm macht eigentlich nur der `ValueError` Sinn, der beim Umwandeln der Zeichenkette in ein Datumsobjekt auftreten kann.
Wenn das Datum nicht umgewandelt werden kann, dann sollte die Funktion auch nicht 0 zurück geben wenn man die Umwandlung in der Funktion lassen würde. Der Aufrufer der Funktion sollte keine besonderen Fehlerwerte testen müssen, dafür gibt es Ausnahmen! Wobei hier noch erschwerend dazu kommt, das 0 gar kein besonderer Fehlerwert ist, sondern ein ganz regulärer Wert sein kann und der Aufrufer so gar nicht unterscheiden kann zwischen einer tatsächlichen 0 für eine gültige Eingabe und einer 0 für eine ungültige Eingabe.
Das berechnen der Jahresdifferenz zwischen Heute und dem Geburtsdatum steht zweimal im Code. Das `int()` bei der einen Berechnung macht wenig Sinn weil die Jahreszahlen bereits ganze Zahlen sind und beim abziehen auch wieder eine ganze Zahl heraus kommt.
Der Name `jahr` ist unpassend für etwas das kein Jahr ist, sondern eine Anzahl von Jahren.
``elif`` als letzter Zweig ist in der Regel ein Warnzeichen. Das kann okay sein, das kann aber auch bedeuten, dass man nicht alle möglichen Fälle abgedeckt hat, denn dann hätte man ja den letzten Fall mit einem ``else`` abhandeln können. Es ist gute Praxis wenn so ``if``-Konstrukt in einem ``elif`` endet, entweder zu dokumentieren warum das okay ist welche Fälle da nicht behandelt werden (sofern das nicht offensichtlich ist), oder aber ein ``else:`` mit einem ``assert False`` anzuhängen, damit man mitbekommt wenn der Fall den man nicht erwartet vielleicht doch mal auftritt. Das erleichtert die Fehlersuche ungemein. In Deinem Code würde die Codestruktur beispielsweise erlauben das die Funktion ganz bis zum Ende durchläuft und keine der expliziten ``return``-Anweisungen ausgeführt wird. Das sollten die Werte verhindern, aber wirklich abgesichert ist der Code dagegen nicht.
Allerdings kann man die jeweils beiden letzten ``elif``-Zweige bei Deinem Code problemlos in ``else``-Zweige umwandeln, denn die Bedingungen decken ja den gesamten Wertebereich ab und die Bedingungen in den beiden letzten ``elif``\s sind redundant und damit überflüssig.
Zwischenstand:
Code: Alles auswählen
if jahre <= 0:
return 0 # Datum liegt in der Zukunft oder heute.
elif heute.month < geburtsdatum.month:
return jahre - 1
elif heute.month > geburtsdatum.month:
return jahre
else:
if heute.day > geburtsdatum.day:
return jahre
elif heute.day < geburtsdatum.day:
return jahre - 1
else:
return jahre
Das innere ``if``/``elif``/``else``-Konstrukt macht in zwei Zweigen das gleiche, das kann man so zusammenfassen bzw. Umstellen das man diese beiden Zweige zusammenfasst und somit nur ein ``if``/``else`` übrig bleibt:
Code: Alles auswählen
if jahre <= 0:
return 0 # Datum liegt in der Zukunft oder heute.
elif heute.month < geburtsdatum.month:
return jahre - 1
elif heute.month > geburtsdatum.month:
return jahre
else:
if heute.day < geburtsdatum.day:
return jahre - 1
else:
return jahre
Wenn man nun das ``return jahre`` hinter dieses ganze Konstrukt schreibt und in den Zweigen die etwas anderes als `jahre` unverändert zurückgeben die Variable `jahre` entsprechend ändert, kann man die Zweige mit ``return jahre`` komplett weg lassen:
Code: Alles auswählen
if jahre <= 0:
jahre = 0 # Datum liegt in der Zukunft oder heute.
elif heute.month < geburtsdatum.month:
jahre -= 1
elif heute.month == geburtsdatum.month:
if heute.day < geburtsdatum.day:
jahre -= 1
return jahre
Die Bedinung vom letzten ``elif`` und dem ``if`` darin kann man zusammenfassen:
Code: Alles auswählen
jahre = heute.year - geburtsdatum.year
if jahre <= 0:
jahre = 0 # Datum liegt in der Zukunft oder heute.
elif heute.month < geburtsdatum.month:
jahre -= 1
elif heute.month == geburtsdatum.month and heute.day < geburtsdatum.day:
jahre -= 1
Und nun kann man noch die beiden letzten ``elif``-Zweige zusammenfassen:
Code: Alles auswählen
if jahre <= 0:
jahre = 0 # Datum liegt in der Zukunft oder heute.
elif (
heute.month < geburtsdatum.month
or heute.month == geburtsdatum.month and heute.day < geburtsdatum.day
):
jahre -= 1
Das erste ``if`` könnte man noch mit Hilfe von `max()` beseitigen. Ungetestet:
Code: Alles auswählen
#!/usr/bin/env python3
from datetime import date as Date, datetime as DateTime
def alter_berechnen(geburtsdatum):
heute = Date.today()
jahre = heute.year - geburtsdatum.year
if (
heute.month < geburtsdatum.month
or heute.month == geburtsdatum.month and heute.day < geburtsdatum.day
):
jahre -= 1
return max(0, jahre)
def main():
while True:
try:
answer = input('Bitte geben Sie das Geburtsdatum ein (dd.mm.yyyy):')
try:
geburtsdatum = DateTime.strptime(answer, '%d.%m.%Y').date()
except ValueError:
print('Die Eingabe ist kein gütiges Datum!')
else:
print(alter_berechnen(geburtsdatum))
except KeyboardInterrupt:
print('Anwendung durch Benutzer abgebrochen...')
if __name__ == '__main__':
main()