@annelie123: Anmerkungen zum Taschenrechnerteil:
Ergänzend zu Namen: Bei `h` und `j` ist auch nicht wirklich klar warum die so heissen. Wenn man generische Namen braucht, würde man eher bei `a` anfangen und mit `b` weiter machen. Wie bei den Funktionen, die dann die vier Rechenoperationen letztlich umsetzen. Da heissen die Argument ja `a` und `b`.
Funktionen werden üblicherweise nach Tätigkeiten benannt. Damit der Leser weiss was sie machen, und sie leichter von eher passiven Werten unterscheidbar sind. `zahl` wäre ein guter Name für allgemeine Zahl, wenn man keinen treffenderen Namen dafür hat, aber es ist kein guter Name für eine Funktion. Die Funktion würde besser `zahl_erfragen()` oder ähnlich heissen, damit man weiss was die tut.
Im `operator`-Modul gibt es Funktionen für die Operatoren. `addieren()`, `subtrahieren()`, und `multiplizieren()` muss man sich also gar nicht selbst schreiben, die gibt es schon. Wenn man die Sonderbehandlung in `dividieren()` nicht macht, gibt es auch da schon eine Funktion im `operator` für. Die Division durch 0 kann man auch durch die Behandlung der Entsprechenden Ausnahme lösen.
Bei den beiden Operanden wird solange nach einer Zahl gefragt, bis der Benutzer tatsächlich eine Zahl eingegeben hat. Bei der Operation wird stattdessen einfach ein "?" ausgegeben wenn der Benutzer etwas anderes als eines der Rechenzeichen eingegeben hat. Da wäre es benutzerfreundlicher auch solange die Frage zu wiederholen bis der Benutzer eine gültige Eingabe gemacht hat. Dann muss man bei einem Tippfehler an der Stelle nicht die beiden Operanden noch mal eingeben.
Es ist nicht gut Daten mehrfach zu wiederholen. Die Rechenzeichen stehen zwei mal im Programm. Da kann man sich vertippen und Fehler machen, beim schreiben des Programms, als auch bei späteren Änderungen/Erweiterungen. Da die Funktionen für die Operationen alle die gleiche Signatur haben, dass heisst alle zwei Operanden als Argumente haben und das Ergebnis der Operation als Rückgabewert, könnte man sich ein Wörterbuch erstellen, wo das Rechenzeichen auf die dazugehörige Funktion abgebildet wird. Dann lässt sich der Code so schreiben, dass die Rechenzeichen nur einmal im Quelltext, in diesem Wörterbuch stehen müssen.
Kommentare sollten dem Leser einen Mehrwert liefern. Faustregel: Man kommentiert nicht *was* der Code macht, denn das steht da ja bereits als Code, sondern warum er das so macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in der Regel was in der Dokumentation der Sprache und der verwendeten Bibliotheken und Module steht. Zu kommentieren, dass ``print("Das Ergebnis lautet:", ergebnis)`` das Ergebnis ausgibt, ist beispielsweise komplett überflüssig.
Taschenrechner (ungetestet):
Code: Alles auswählen
#!/usr/bin/env python3
from operator import (
add as addieren,
mul as multiplizieren,
sub as subtrahieren,
truediv as dividieren,
)
OPERATOR_ZU_OPERATION = {
"+": addieren,
"-": subtrahieren,
"*": multiplizieren,
"/": dividieren,
}
def zahl_erfragen(eingabeaufforderungstext):
while True:
try:
return float(input(eingabeaufforderungstext))
except ValueError:
print("Bitte gib eine gültige Zahl ein!")
def operation_erfragen(operator_zu_operation):
operatoren_text = ", ".join(operator_zu_operation.keys())
eingabeaufforderungstext = (
f"Gebe ein Rechenzeichen ein ({operatoren_text}): "
)
while True:
try:
return operator_zu_operation[input(eingabeaufforderungstext)]
except KeyError:
print("Bitte gib ein gültiges Rechenzeichen ein!")
def main():
a = zahl_erfragen("Gebe den ersten Operanden ein: ")
b = zahl_erfragen("Gebe den zweiten Operanden ein: ")
operation = operation_erfragen(OPERATOR_ZU_OPERATION)
try:
ergebnis = operation(a, b)
except ZeroDivisionError:
print("Fehler: Division durch Null ist nicht erlaubt!")
else:
print("Das Ergebnis lautet:", ergebnis)
if __name__ == "__main__":
main()
Anmerkungen zum Tic-Tac-Toe:
Konstantennamen werden per Konvention KOMPLETT_GROSS geschrieben.
Wenn man einen Namen hat, den man aus syntaktischen Gründen braucht, dessen Wert aber keine Rolle spielt, dann verwendet man per Konvention den Namen `_`. Dann weiss der Leser, dass er diesen Namen nicht weiter beachten muss, und dass es Absicht ist, dass der Wert nicht verwendet wird. Das betrifft hier `i`.
Statt die 9 als literale Zahl für die Anzahl Schleifendurchläufe in den Quelltext zu schreiben, könnte man hier die Länge von `grid()` ermitteln. Dann kann man das beispielsweise von Tic-Tac-Toe zu „Vier gewinnt“ erweitern, ohne die Zeile noch einmal ändern zu müssen.
Der Rückgabewert von `play()` könnte einfacher sein. Statt `None` oder der Indexwerte von Gewinnerfeldern, könnte man auch nur False und True zurückgeben, denn der Aufrufer macht mit den Indexwerten nichts.
Das hin und her wandeln der Eingabe für eine Zelle zwischen ganzer Zahl und Zeichenkette und das lineare suchen nach der Zahl in `grid`, obwohl die Zahl ja nur an einer Stelle in `grid()` stehen kann, die aus der Zahl selbst ganz einfach errechenbar ist (-1), ist unnötig umständlich und damit schwerer verständlich als es sein müsste.
Zwischenstand (ungetestet):
Code: Alles auswählen
#!/usr/bin/env python3
WINNING_COMBOS = [
# Waagerecht.
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
# Senkrecht.
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
# Diagonal.
[0, 4, 8],
[2, 4, 6],
]
PLAYER_1 = "X"
PLAYER_2 = "O"
def play(grid, player):
print()
print(" " + " | ".join(grid[:3]))
print("---+---+---")
print(" " + " | ".join(grid[3:6]))
print("---+---+---")
print(" " + " | ".join(grid[6:]))
while True:
try:
i = int(input(f"Gib eine Zahl für {player} ein: ")) - 1
except ValueError:
pass
else:
if 0 <= i < len(grid) and grid[i] not in {PLAYER_1, PLAYER_2}:
grid[i] = player
break
print("Gib eine Zahl von einer freien Zelle ein!")
return any(
all(grid[i] == player for i in combo) for combo in WINNING_COMBOS
)
def main():
grid = list(map(str, range(1, 10)))
player = PLAYER_1
#
# In jedem Schleifendurchlauf wird ein Feld belegt. Wenn alle Felder belegt
# sind, ist das Spiel unentschieden.
#
for _ in range(len(grid)):
if play(grid, player):
print(f"Spieler {player} hat gewonnen!")
break
player = PLAYER_1 if player == PLAYER_2 else PLAYER_2
else:
print("Unentschieden")
if __name__ == "__main__":
main()