@derbozz: Da Einrückung in Python-Code wichtig ist, verwende doch bitte Code-Tags für Quelltext im Beitrag.
Vorschläge: Unbenutzte Importe und auskommentierten Quelltext sollte man entsorgen. Genauso unbenutzen Code wie die `speichern()`-Funktion. Das ist nur unnötiger Ballast für den Leser.
Ganz generell kannst Du mal einen Blick in den
Style Guide for Python Code werfen.
Namen sollen dem Leser vermitteln welche Bedeutung der Wert der daran gebunden ist, im Programm hat. Einbuchstabige Namen tun das nur selten und sollten auf Zählvariablen und wirklich kurzen, überschaubaren Kontext, beispielsweise „list comprehensions” oder Generatorausdrücke, beschränkt werden.
Wie soll man bei dem Namen `adatum` darauf kommen dass der Wert dahinter die Länge einer Zeichenkette ist? Wie bist Du auf diesen Namen gekommen? Warum bindest Du diesen Wert überhaupt an einen Namen? Der wird nur an einer Stelle verwendet, und an dieser einen Stelle könnte man auch einfach die `len()`-Funktion anwenden. Und dann wird `adatum` nach der Eingabe auch noch an etwas ganz anderes gebunden, aber überhaupt nicht mehr verwendet‽
Kommentare sind dazu da dem Leser Informationen zu vermitteln die nicht schon trivial aus dem Quelltext den sie kommentieren zu ersehen ist. Dazu müssen sie sprachlich korrekt sein. Was bei ``#zahlen kontrollen`` nicht der Fall sein. Die anderen Kommentare im Programm wiederholen bis auf eine Ausnahme einfach nur den Variablennamen der in der nächsten Zeile an einen Wert gebunden wird. Welchen Sinn soll das haben? Wenn man in einem Kommentar einen Variablennamen erklärt, sollte man noch einmal über den Namen nachdenken, ob der nicht so gut sein könnte, dass man sich den Kommentar sparen kann. Statt einen Wert `name` zu nennen und zu kommentieren, dass es sich um den Namen des Turniers handelt, hätte man ihn ohne Kommentar auch einfach `turniername` nennen können.
Auf Modulebene sollte man nur Definitionen von Konstanten, Funktionen, und Klassen stehen haben und nicht Hauptprogramm direkt auf Modulebene. Üblicherweise kommt das in eine Funktion mit dem Namen `main()`. Wenn man dann auch noch Hauptprogramm und Funktionsdefinitionen vermischt, wird es schnell unübersichtlich.
Die `ist_zahl()`-Funktion müsste korrekter `ist_gleitkommazahl()` heissen. Dann fällt auch eher auf, dass sie ungeeignet ist, wo sie im Programm verwendet wird, denn dort werden überall ganze Zahlen erwartet.
Innerhalb der Eingabe eines Wertes gibt es viele Wiederholungen und der gesamte Code zur Eingabe eines Wertes wiederholt sich fast identisch im Quelltext. So etwas sollte man vermeiden. Durch geschicktere Strukturierung des Codes und durch Funktionen und/oder Schleifen wo das gemeinsame von den Unterschieden getrennt wird, so dass man den gemeinsamen Teil nur einmal schreiben muss. Eine Zeile wie ``datum = raw_input('Wann soll das Turnier stattfinden?(ttmmjj)')`` sollte nicht *drei* mal identisch in einem Quelltextabschnitt stehen, sondern nur *einmal*. Wenn man den Text ändern möchte muss man das dann zum Beispiel nur noch an einer Stelle machen und nicht an drei, mit der Gefahr dass man eine vergisst, oder nicht alle drei Stellen gleich verändert.
Diese beiden ersten Quelltextabschnitte, welche die Eingabe sowohl auf Länge als auch auf (Gleitkomma)Zahl testen sind übrigens fehlerhaft. Wenn man etwas mit der passenden Länge eingibt was aber keine Gleitkommazahl ist, dann kommt man in die zweite Schleife, die dann zwar prüft das man nur Gleitkommazahlen eingeben kann, aber die Länge der Eingabe wird überhaupt nicht mehr beachtet. Man könnte bei der Eingabe des Datums also zum Beispiel aabbcc eingeben und dann 42 und das wird dann einfach übernommen obwohl es nicht sechs Zeichen lang ist.
Man könnte eine Eingabe mit beiden Prüfungen und ohne ganze Quelltextzeilen zu wiederholen zum Beispiel so schreiben:
Code: Alles auswählen
while True:
datum = raw_input('Wann soll das Turnier stattfinden? (ttmmjj)')
if len(datum) == 6 and datum.isdigit():
break
print 'Eingabe entspricht nicht dem erwarteten Format (ttmmjj).'
Vergleiche mit literalen `True` oder `False` sind schlechter Stil, weil in der Regel überflüssig. Wenn man auf einen gegenteiligen Wahrheitswert testen möchte, kann man ``not`` verwenden. Also anstatt ``if is_spam(value) == False:`` besser ``if not is_spam(value):``.
Die ersten beiden Eingaben sind Zeichenketten die nur aus Ziffern bestehen dürfen und eine feste Länge haben. Dafür kann man eine Funktion schreiben, und muss den Code dann nicht zweimal schreiben. Die beiden Quelltextabschnitte unterscheiden sich im Grunde ja nur durch den Inhalt des Prompts für den Benutzer und dem Muster und der Länge der korrekten Eingabe.
Die beiden folgenden Eingaben sind ganze Zahlen. Am besten positiv und grösser 0 würde auch Sinn machen. Auch diese Eingabe samt Tests kann man in einer Funktion kapseln.
`tag`, `monat`, `jahr`, `stunde`, und `minute` werden zwar definiert, aber nirgends verwendet. Weg damit. Genau so das Wörterbuch `d` das zwar gefüllt, aber nicht verwendet wird. Da ist mir sowieso nicht klar was der Sinn von den Schlüsseln sein soll.
Das Speichern der Daten könnte man in eine eigene Funktion auslagern, möglichst ohne Benutzerinteraktion. Das heisst die Teamnamen sollten ausserhalb der Speicherfunktion vom Benutzer erfragt werden.
Ob CSV so besonders gut geeignet ist, wage ich zu bezweifeln. Zumindest wenn man die Daten auch wieder in einem eigenen Programm einlesen möchte ist das eher für „regelmässige” Daten geeignet. Ansonsten muss man unnötige Arbeit in das auswerten und testen der Daten stecken, die man bei Formaten wie JSON beispielsweise nicht hätte, weil sich der Parser dort zumindest um das Format und die Grunddatentypen kümmert.
Ich komme als Zwischenergebnis jedenfalls auf so etwas (untetestet):
Code: Alles auswählen
#!/usr/bin/python
import csv
def ask_digit_string(prompt, pattern):
prompt = '{0} ({1})'.format(prompt, pattern)
while True:
result = raw_input(prompt)
if len(result) == len(pattern) and result.isdigit():
return result
print 'Eingabe entspricht nicht dem erwarteten Format', pattern
def ask_count(prompt):
while True:
try:
result = int(raw_input(prompt))
except ValueError:
print 'Bitte eine ganze Zahl eingeben.'
else:
if result >= 1:
return result
else:
print 'Die Anzahl muss mindestens 1 betragen.'
def main():
turniername = raw_input('Wie soll das Turnier heissen?')
datum = ask_digit_string('Wann soll das Turnier stattfinden?', 'ttmmjj')
startzeit = ask_digit_string('Wie spaet soll das Turnier beginnen?', 'hhmm')
anzahlteams = ask_count('Wie viele Teams nehmen teil?')
anzahlfelder = ask_count('Wie viele Felder stehen zur verfuegung?')
dateiname = '{0}-{1}.csv'.format(turniername, datum)
with open(dateiname, 'wb') as out_file:
writer = csv.writer(out_file, delimiter=';')
writer.writerows(
[
['Name', 'Datum', 'Startzeit', 'Anzahlfelder'],
[turniername, datum, startzeit, anzahlfelder],
[],
['Teams'],
]
)
for teamnr in xrange(1, anzahlteams + 1):
teamname = raw_input('Wie heisst das {0}. Team?'.format(teamnr))
writer.writerow([teamname])
print 'Gespeichert unter dem Namen', dateiname
if __name__ == '__main__':
main()
Was man als nächstes angehen könnte wären die passenden Datentypen für Datum und Zeit zu verwenden (`datetime`-Modul) und entsprechende Eingabefunktionen zu schreiben. Denn im Moment kann der Benutzer ja noch den 99 Tag im 42 Monat als Datum angeben oder 33 Uhr 90 als Uhrzeit.