Programmierstyl
Liebes Forum,
dies ist mein erster Post. Ich habe nun mein erstes Python-Programm geschrieben und möchte gerne Feedback von euch erhalten. Ist mein Programmierstyl gut so wie er ist oder kann ich bestimmte Sachen deutlich "schöner" machen? Ich freue mich über jede Kritik und Anmerkung.
Hier der Link zum Respository auf Github:
https://github.com/Domroon/RockPaperScissor
Ich hoffe der Link reicht aus. Wenn nicht, dann lasse ich mich gerne eines besseren belehren.
Das Spiel ist noch nicht ganz fertig aber schon lauffähig. Eine einzige Runde lässt sich damit schon spielen, was sich natürlich noch ändern soll. Auch ist eine Bestenliste geplant. Die Readme wird auch noch besser... Versprochen
Ich freue mich auf eure Beiträge.
Domroon
dies ist mein erster Post. Ich habe nun mein erstes Python-Programm geschrieben und möchte gerne Feedback von euch erhalten. Ist mein Programmierstyl gut so wie er ist oder kann ich bestimmte Sachen deutlich "schöner" machen? Ich freue mich über jede Kritik und Anmerkung.
Hier der Link zum Respository auf Github:
https://github.com/Domroon/RockPaperScissor
Ich hoffe der Link reicht aus. Wenn nicht, dann lasse ich mich gerne eines besseren belehren.
Das Spiel ist noch nicht ganz fertig aber schon lauffähig. Eine einzige Runde lässt sich damit schon spielen, was sich natürlich noch ändern soll. Auch ist eine Bestenliste geplant. Die Readme wird auch noch besser... Versprochen
Ich freue mich auf eure Beiträge.
Domroon
Am besten postet Du Code hier im Forum direkt in Code Tags (</>-Knopf).
Module schreibt man wie Variablen- oder Funktionsnamen komplett klein. Wenn ein Projekt mehr als ein Modul hat, packt man alles in ein Pakage.
Du hast aber eine Klasse pro Datei. Das macht man in Python nicht. Bei so einem kleinen Projekt würde man noch alles in eine Datei packen.
Auf oberster Ebene sollte kein ausführbarer Code stehen, daher gehören die beiden Zeilen in app.py in eine Funktion, die üblicherweise main genannt wird.
In User.py:
- man schreibt keine Trivialen Getter oder Setter.
- in set_choice hast Du dreimal den selben if-Block, nur die Bedingung ist unterschiedlich, also kann man das alles in eines zusammenfassen.
- wenn der einzige Fall, wo eine Exception auftritt, selbst erzeugt ist, dann würde ich ein normales if benutzen.
- andererseits sollte keine Fehlerbehandlung in solch einer Funktion stehen, so dass der ValueError gar nicht abgefangen werden sollte.
Fortgeschrittene würden choice als Property implementieren.
Bei Npc.py:
- keine Ahnung was Npc bedeuten soll. Benutze keine Abkürzungen, sondern sprechende Namen.
- bei randint: Benutze keine fixen Werten würde, sondern ermittle sie dynamisch aus der Länge der Liste, noch besser random.choice benutzen.
- move wäre besser eine Konstante.
In Game.py:
Du solltest auf Falscheingaben des Nutzers reagieren.
`is_winner` wird aufgerufen ohne dass man etwas mit dem Rückgabewert macht.
is_winner sollte alle Fälle abprüfen. So wie es jetzt da steht überrascht es den Leser, dass manche if-Zweige gar keinen Rückgabewert haben.
Der Code ist im Moment noch unflexibler, als er sein müßte. Es wäre kein Problem, dass zwei Computer oder zwei Menschen gegeneinander spielen.
Dass so oft die Strings "scissor", "paper" und "rock" auftauchen, ist eine potentielle Fehlerquelle. Ein Enum ist die übliche Lösung dafür. Der Algorithmus würde auch deutlich einfacher, wenn man die Namen durch numerische Werte ersetzt.
startscreen wird nie aufgerufen.
have_win wird gar nicht benutzt. Und choice ist auch nicht wirklich ein Zustand der Klassen User und Npc sondern eher eine Methode: get_choice.
Module schreibt man wie Variablen- oder Funktionsnamen komplett klein. Wenn ein Projekt mehr als ein Modul hat, packt man alles in ein Pakage.
Du hast aber eine Klasse pro Datei. Das macht man in Python nicht. Bei so einem kleinen Projekt würde man noch alles in eine Datei packen.
Auf oberster Ebene sollte kein ausführbarer Code stehen, daher gehören die beiden Zeilen in app.py in eine Funktion, die üblicherweise main genannt wird.
Code: Alles auswählen
def main():
game = Game()
game.round()
if __name__ == '__main__':
main()
- man schreibt keine Trivialen Getter oder Setter.
- in set_choice hast Du dreimal den selben if-Block, nur die Bedingung ist unterschiedlich, also kann man das alles in eines zusammenfassen.
- wenn der einzige Fall, wo eine Exception auftritt, selbst erzeugt ist, dann würde ich ein normales if benutzen.
- andererseits sollte keine Fehlerbehandlung in solch einer Funktion stehen, so dass der ValueError gar nicht abgefangen werden sollte.
Code: Alles auswählen
class User:
def __init__(self, name):
self.score = 0
self.name = name
self.choice = "nothing"
self.have_win = False
def set_one_point(self):
self.score += 1
def set_choice(self, move):
if move not in ["rock", "paper", "scissor"]:
raise ValueError('You can choose between "rock", "paper" or "scissor"')
self.choice = move
Bei Npc.py:
- keine Ahnung was Npc bedeuten soll. Benutze keine Abkürzungen, sondern sprechende Namen.
- bei randint: Benutze keine fixen Werten würde, sondern ermittle sie dynamisch aus der Länge der Liste, noch besser random.choice benutzen.
- move wäre besser eine Konstante.
Code: Alles auswählen
import random
class Npc:
MOVE = [
"rock",
"paper",
"scissor"
]
def __init__(self):
self.score = 0
self.choice = "nothing"
def set_one_point(self):
self.score += 1
def set_choice(self):
self.choice = random.choice(self.MOVE)
Du solltest auf Falscheingaben des Nutzers reagieren.
`is_winner` wird aufgerufen ohne dass man etwas mit dem Rückgabewert macht.
is_winner sollte alle Fälle abprüfen. So wie es jetzt da steht überrascht es den Leser, dass manche if-Zweige gar keinen Rückgabewert haben.
Code: Alles auswählen
import time
class Game:
def __init__(self):
self.user = User("Nobody")
self.npc = Npc()
def startcreen(self):
print('Welcome to the Game "Rock, Paper or Scissor!"')
self.user.name = input("Name: ")
print(f"Hello {self.user.name}! Lets start the Game!")
def round(self):
# user and npc take their choices
while True:
try:
self.user.set_choice(input("Please take your choice: "))
except ValueError as error:
print(error)
self.npc.set_choice()
self.round_animation()
print(f"{self.user.choice} against {self.npc.choice}")
winner = self.get_winner()
if winner is None:
print("Undecided!")
else:
winner.set_one_point()
if winner is self.user:
print("You win!")
else:
print("You loose!")
print(f"User score: {self.user.score}")
print(f"Npc score: {self.npc.score}")
def round_animation(self):
time.sleep(1)
print("Rock!")
time.sleep(1)
print("Paper!")
time.sleep(1)
print("Scissor!")
time.sleep(1)
def get_winner(self):
choice_user = self.user.choice
choice_npc = self.npc.choice
if choice_user == "rock":
if choice_npc == "paper":
return self.npc
elif choice_npc == "scissor":
return self.user
else:
return None
elif choice_user == "paper":
if choice_npc == "scissor":
return self.npc
elif choice_npc == "stone":
return self.user
else:
return None
elif choice_user == "scissor":
if choice_npc == "rock":
return self.npc
elif choice_npc == "paper":
return self.user
else:
return None
else:
raise ValueError()
Dass so oft die Strings "scissor", "paper" und "rock" auftauchen, ist eine potentielle Fehlerquelle. Ein Enum ist die übliche Lösung dafür. Der Algorithmus würde auch deutlich einfacher, wenn man die Namen durch numerische Werte ersetzt.
startscreen wird nie aufgerufen.
have_win wird gar nicht benutzt. Und choice ist auch nicht wirklich ein Zustand der Klassen User und Npc sondern eher eine Methode: get_choice.
Hallo Sirirus3,
Vielen Dank für diese schnelle und umfangreiche Antwort Ich werde in Kürze Deine Verbesserungsvorschläge umsetzten und mich dann wieder melden
Vielen Dank für diese schnelle und umfangreiche Antwort Ich werde in Kürze Deine Verbesserungsvorschläge umsetzten und mich dann wieder melden
Ich sehe ein, dass die natürlich kein großes Programm ist und diese Dateistrukturen wahrscheinlich übertrieben sind. Ehrlich gesagt möchte ich aber in die Zukunft schauen und meine Programme von vorn herein so schreiben wie man auch größere Programme schreiben würde. Vielen Dank für den Hinweis meine Module in Packages zu organisieren Die Modulnamen sind nun auch klein geschrieben. Wieso darf ich eine einzelne Klasse nicht in einer Datei schreiben? Ich finde das sehr übersichtlich und in vielen größeren Python-Projekten in Python ist es auch so organisiert. Gibt es einen anderen Grund dies nicht auf diese Art und Weise zu tun?
In user.py:
- ohne diese Trivialen Getter und Setter Dateien würde mein Programm nicht mehr funktionieren, wie soll ich denn stattdessen auf die Attribute des Objektes zugreifen?
- deine verbesserte version der "set_choice"-methode habe ich nun übernommen, vielen dank dafür
- fängt man die Fehler immer besser auf einer höheren ebene ab? In der game.py-klasse habe ich den Fehler nun in einer "höheren ebene" wie folgt abgefangen:
/<
# user and npc take their choices
while True:
try:
self.user.set_choice(input("Please take your choice: "))
break
except ValueError:
print('You can choose between "rock", "paper" or "scissor"')
/> (ich verstehe diese Codetags nicht )
In game.py:
- wie oben beschrieben reagiere ich nun auf falscheingaben, habe aber nun doch deine lösung für falscheingaben benutzt
In user.py:
- ohne diese Trivialen Getter und Setter Dateien würde mein Programm nicht mehr funktionieren, wie soll ich denn stattdessen auf die Attribute des Objektes zugreifen?
- deine verbesserte version der "set_choice"-methode habe ich nun übernommen, vielen dank dafür
- fängt man die Fehler immer besser auf einer höheren ebene ab? In der game.py-klasse habe ich den Fehler nun in einer "höheren ebene" wie folgt abgefangen:
/<
# user and npc take their choices
while True:
try:
self.user.set_choice(input("Please take your choice: "))
break
except ValueError:
print('You can choose between "rock", "paper" or "scissor"')
/> (ich verstehe diese Codetags nicht )
In game.py:
- wie oben beschrieben reagiere ich nun auf falscheingaben, habe aber nun doch deine lösung für falscheingaben benutzt
@Domroon: Auf die Attribute greift man zu, indem man auf die Attribute zugreift.
`set_one_point` ist ein unpassender Name, finde ich. Ich würde erwarten, dass die Methode `score` auf 1 setzt, nicht um 1 inkrementiert. `add_point` wäre passender.
`set_one_point` ist ein unpassender Name, finde ich. Ich würde erwarten, dass die Methode `score` auf 1 setzt, nicht um 1 inkrementiert. `add_point` wäre passender.
@narpfel: Das macht Sinn Ich habe nur aus dem Informatikunterricht in der Schule gelernt (dort haben wir mit Java programmiert), dass man generell diese getter und setter- Methoden schreiben sollte und nicht direkt auf die attribute zugreifen sollte... Warum weiß ich nicht mehr Aber gut, dass es in Python einfacher ist Gute Anregung, den Namen von "set_one_point" werde ich nun ändern
PS: Ich habe nun viele Anpassungen vorgenommen und meine erste Version released Schaut doch mal nochmal in mein Repository: https://github.com/Domroon/RockPaperScissor
PS: Ich habe nun viele Anpassungen vorgenommen und meine erste Version released Schaut doch mal nochmal in mein Repository: https://github.com/Domroon/RockPaperScissor
@Domroon: Python ist nicht Java.
In Java muss man Getter und Setter schreiben, wenn man sich die Möglichkeit offen halten möchte, ein Klasse später nochmal zu verändern, ohne den gesamten Code anzupassen, der die Klasse benutzt. Wenn du irgendwann merkst, dass du ein Attribut gar nicht brauchst (zum Beispiel, weil du es aus anderen Attributen berechnen kannst), musst du in Java alle Stellen ändern, wo das Attribut verwendet wird.
In Python wird mit dem Problem anders umgegangen: Erstens YAGNI. Zweitens gibt es `property`, mit dem man Getter und Setter quasi nachträglich einbauen kann, ohne vorsorglich den Code mit `get_foo` und `set_bar` unleserlich zu machen.
In Java muss man Getter und Setter schreiben, wenn man sich die Möglichkeit offen halten möchte, ein Klasse später nochmal zu verändern, ohne den gesamten Code anzupassen, der die Klasse benutzt. Wenn du irgendwann merkst, dass du ein Attribut gar nicht brauchst (zum Beispiel, weil du es aus anderen Attributen berechnen kannst), musst du in Java alle Stellen ändern, wo das Attribut verwendet wird.
In Python wird mit dem Problem anders umgegangen: Erstens YAGNI. Zweitens gibt es `property`, mit dem man Getter und Setter quasi nachträglich einbauen kann, ohne vorsorglich den Code mit `get_foo` und `set_bar` unleserlich zu machen.
@narpfel, @Sirius3
Ich habe den Code nun so angepasst, dass ich alle (wie Sirius3 schon schrieb) trivialen getter und setter-Methoden gelöscht habe und nun mit "beispielobjekt.attribut" auf das jeweilige Attribut zugreife. Lediglich wo ein trivialer Zugriff nicht möglich ist habe ich das entsprechende Attribut von public auf private (mit dem Underscore "_"vor dem Attribut) geändert und mittels "@property" als getter oder setter methoden implementiert.
siehe z. B. hier in der User-Klasse:
Ich habe also alle trivialen getter und setter gelöscht und nur dort wo es notwendig ist (notwendig heißt z. B. ein Wert für ein Attribut kommt z. B. vom Benutzer und es muss vorher geprüft werden ob dieser Wert ein gültiger ist) habe ich property benutzt
Meine FrageFragen: Habe ich den Sinn hinter property richtig verstanden oder habt ihr dem etwas hinzuzufügen oder zu korrigieren? Wäre property auch bei der Methode "add_point" sinnvoll?
Danke euch schonmal und schönen Tag euch
Ich habe den Code nun so angepasst, dass ich alle (wie Sirius3 schon schrieb) trivialen getter und setter-Methoden gelöscht habe und nun mit "beispielobjekt.attribut" auf das jeweilige Attribut zugreife. Lediglich wo ein trivialer Zugriff nicht möglich ist habe ich das entsprechende Attribut von public auf private (mit dem Underscore "_"vor dem Attribut) geändert und mittels "@property" als getter oder setter methoden implementiert.
siehe z. B. hier in der User-Klasse:
Code: Alles auswählen
class User:
def __init__(self, name):
self.score = 0
self.name = name
self._choice = "nothing"
self.have_win = False
def add_point(self):
self.score = self.score + 1
@property
def choice(self):
return self._choice
@choice.setter
def choice(self, move):
if move not in ["rock", "paper", "scissor"]:
raise ValueError('You can choose between "rock", "paper" or "scissor"')
self._choice = move
Meine FrageFragen: Habe ich den Sinn hinter property richtig verstanden oder habt ihr dem etwas hinzuzufügen oder zu korrigieren? Wäre property auch bei der Methode "add_point" sinnvoll?
Danke euch schonmal und schönen Tag euch
Man kann in Python auch von außerhalb der Klasse auf Variablen mit einem Unterstrich davor zugreifen. So etwas wie private exestiert nicht. Der Unterstrich ist eher eine Konvention, dass man die Variable nicht benutzen soll bzw. nicht drauf zugreifen soll.
Hier noch der Teil aus der Doku: hier
Hier noch der Teil aus der Doku: hier
Aus dem Repository: in npc.py (immer noch ein schlechter Name und immer noch eine zu feingliedrige Aufteilung in Dateien) hast Du ein Property score, das eigentlich überflüssig ist. Auch choice ist hier kein Property.
Und hier hast Du ja auch schon eine Liste mit möglichen Werten, die Du in User wiederverwenden solltest:
Das zeigt auch schön, dass eine zu starke Aufteilung in Dateien es schwierig macht, gemeinsame Daten zu nutzen.
Wie schon in meinem ersten Post geschrieben, macht das unterschiedliche Interface von User und Npc es schwierig, die beiden einfach auszutauschen, was den schönen Effekt hätte, dass man wählen könnte ob zwei Personen oder zwei Computer gegeneinander spielen.
In app.py wird jetzt ein Design-Problem noch deutlicher: du erzeugst einen User mit Dummy-Namen, dem Du erst viel später an ganz anderer Stelle einen richtigen Namen gibst. Das macht das Programm sehr unübersichtlich und fehleranfällig: wie heißt der User wirklich? Wo wird da was geändert, und wird das überhaupt immer geändert? Ein Grundsatz ist: benutze niemals Dummy-Werte. Verwende von Anfang an den richtigen Wert für eine Variable. Es gibt Fälle, wo explizit erlaubt ist, dass eine Variable auch "keinen Wert" haben kann, das ist dann None; das muß aber auch explizit an jeder Stelle mit berücksichtigt werden. Hier ist das aber nicht der Fall.
`max_score` wird gar nicht berücksichtigt; hier ist einer der Fälle, wo eine while-True-Schleife falsch eingesetzt wird (normalerweise ist es immer umgekehrt).
Code: Alles auswählen
POSSIBLE_MOVES = ["rock", "paper", "scissor"]
class Npc:
def __init__(self):
self.score = 0
self.choice = "nothing"
def add_point(self):
self.score += 1
def make_choice(self):
self.choice = random.choice(POSSIBLE_MOVES)
Code: Alles auswählen
class User:
def __init__(self, name):
self.score = 0
self.name = name
self._choice = "nothing"
def add_point(self):
self.score += 1
@property
def choice(self):
return self._choice
@choice.setter
def choice(self, move):
if move not in POSSIBLE_MOVES
raise ValueError(f'You can choose between {" / ".join(POSSIBLE_MOVES)}')
self._choice = move
Wie schon in meinem ersten Post geschrieben, macht das unterschiedliche Interface von User und Npc es schwierig, die beiden einfach auszutauschen, was den schönen Effekt hätte, dass man wählen könnte ob zwei Personen oder zwei Computer gegeneinander spielen.
In app.py wird jetzt ein Design-Problem noch deutlicher: du erzeugst einen User mit Dummy-Namen, dem Du erst viel später an ganz anderer Stelle einen richtigen Namen gibst. Das macht das Programm sehr unübersichtlich und fehleranfällig: wie heißt der User wirklich? Wo wird da was geändert, und wird das überhaupt immer geändert? Ein Grundsatz ist: benutze niemals Dummy-Werte. Verwende von Anfang an den richtigen Wert für eine Variable. Es gibt Fälle, wo explizit erlaubt ist, dass eine Variable auch "keinen Wert" haben kann, das ist dann None; das muß aber auch explizit an jeder Stelle mit berücksichtigt werden. Hier ist das aber nicht der Fall.
`max_score` wird gar nicht berücksichtigt; hier ist einer der Fälle, wo eine while-True-Schleife falsch eingesetzt wird (normalerweise ist es immer umgekehrt).
Code: Alles auswählen
MAX_SCORE = 3
def main():
print('Welcome to the Game "Rock , Paper or Scissor!"')
name = input("Name: ")
print(f"Hello {name}! Lets start the Game!")
user = User(name)
npc = Npc()
game = Game(user, npc)
while user.score < MAX_SCORE and npc.score < MAX_SCORE:
game.round()
print("Thank you for gaming!")
if __name__ == '__main__':
main()
@Jankie:
achso, gut zu wissen, vielen Dank für diese Anmerkung
@Sirius3:
Ich will parallel mit git und github warm werden. Deshalb habe ich einen neuen Branch "Sirius3_deep_improvements" erstellt.
Ich möchte hier kurz die letzten commits schreiben, ich hoffe das ist ok für alle
(ich buchstabiere diese durch um eine Frage zum jeweiligen commit zu stellen):
a- rename npc to computer
b- delete computer.py-, game.py-, user.py - files and put them all in app.py
c- delete unnecessary files
d- put POSSIBLE_MOVES outside from Computer-class
e- improve the main-method
zu a:
Völlig verständlich.
zu b:
Okay ich verstehe den Sinn, dass die Klassen in eine gemeinsame Datei gepackt werden sollten. Klar so ist es einfacher gemeinsame Daten zu nutzen. Nur jetzt muss ich immer hoch und runter scrollen nur um zu gucken welche Methoden in der jeweiligen Klasse zu Verfügung stehen. Das Projekt ist klein. Aber gehen wir mal davon aus, dass das Programm jetzt immer weiter wächst: Wäre es dann ab einem bestimmten Punkt sinnvoll die Klassen in eigene Dateien zu packen oder wie würde man dann vorgehen?
zu d:
Ist klar
zu d: Ich bin verwirrt. Ich hatte schon ein wenig mit C++ und Java zu tun. Dort war es gefühlt immer eine Todessünde wenn man eine globale Variable erstellt hat. Warum macht man das hier in Python anders? Oder bin ich gerade auf einem völlig falschen Zug?
zu e:
Versteh ich glaube ich
Hier der aktuelle Code von Github, welcher nur noch aus der Datei app.py besteht:
achso, gut zu wissen, vielen Dank für diese Anmerkung
@Sirius3:
Ich will parallel mit git und github warm werden. Deshalb habe ich einen neuen Branch "Sirius3_deep_improvements" erstellt.
Ich möchte hier kurz die letzten commits schreiben, ich hoffe das ist ok für alle
(ich buchstabiere diese durch um eine Frage zum jeweiligen commit zu stellen):
a- rename npc to computer
b- delete computer.py-, game.py-, user.py - files and put them all in app.py
c- delete unnecessary files
d- put POSSIBLE_MOVES outside from Computer-class
e- improve the main-method
zu a:
Völlig verständlich.
zu b:
Okay ich verstehe den Sinn, dass die Klassen in eine gemeinsame Datei gepackt werden sollten. Klar so ist es einfacher gemeinsame Daten zu nutzen. Nur jetzt muss ich immer hoch und runter scrollen nur um zu gucken welche Methoden in der jeweiligen Klasse zu Verfügung stehen. Das Projekt ist klein. Aber gehen wir mal davon aus, dass das Programm jetzt immer weiter wächst: Wäre es dann ab einem bestimmten Punkt sinnvoll die Klassen in eigene Dateien zu packen oder wie würde man dann vorgehen?
zu d:
Ist klar
zu d: Ich bin verwirrt. Ich hatte schon ein wenig mit C++ und Java zu tun. Dort war es gefühlt immer eine Todessünde wenn man eine globale Variable erstellt hat. Warum macht man das hier in Python anders? Oder bin ich gerade auf einem völlig falschen Zug?
zu e:
Versteh ich glaube ich
Hier der aktuelle Code von Github, welcher nur noch aus der Datei app.py besteht:
Code: Alles auswählen
import random
import time
POSSIBLE_MOVES = ["rock", "paper", "scissor"]
MAX_SCORE = 3
class Game:
def __init__(self, user, computer):
self.user = user
self.computer = computer
def round(self):
# user and computer take their choices
while True:
try:
self.user.choice = input("Please take your choice: ")
break
except ValueError as error:
print(error)
self.computer.make_choice()
self.round_animation()
print(f"{self.user.choice} against {self.computer.choice}")
winner = self.get_winner()
if winner is None:
print("Undecided!")
else:
winner.add_point()
if winner is self.user:
print("You win!")
else:
print("You loose!")
print(f"User score: {self.user.score}")
print(f"computer score: {self.computer.score}")
def round_animation(self):
speed = 0.5
time.sleep(speed)
print("Rock!")
time.sleep(speed)
print("Paper!")
time.sleep(speed)
print("Scissor!")
time.sleep(speed)
def get_winner(self):
choice_user = self.user.choice
choice_computer = self.computer.choice
if choice_user == "rock":
if choice_computer == "paper":
return self.computer
elif choice_computer == "scissor":
return self.user
else:
return None
elif choice_user == "paper":
if choice_computer == "scissor":
return self.computer
elif choice_computer == "rock":
return self.user
else:
return None
elif choice_user == "scissor":
if choice_computer == "rock":
return self.computer
elif choice_computer == "paper":
return self.user
else:
return None
else:
raise ValueError()
class User:
def __init__(self, name):
self.score = 0
self.name = name
self._choice = "nothing"
self.have_win = False
def add_point(self):
self.score += 1
@property
def choice(self):
return self._choice
@choice.setter
def choice(self, move):
if move not in POSSIBLE_MOVES:
raise ValueError(f'You can choose between {" / ".join(POSSIBLE_MOVES)}')
self._choice = move
class Computer:
def __init__(self):
self.score = 0
self.choice = "nothing"
def add_point(self):
self.score = self.score + 1
def make_choice(self):
self.choice = random.choice(POSSIBLE_MOVES)
def main():
print('Welcome to the Game "Rock, Paper or Scissor!"')
name = input("Name: ")
print(f"Hello {name}! Lets start the Game!")
user = User(name)
computer = Computer()
game = Game(user, computer)
while user.score < MAX_SCORE and computer.score < MAX_SCORE:
game.round()
print("Thank you for gaming!")
if __name__ == '__main__':
main()
zu b) natürlich wird eine Datei irgendwann zu groß. Wobei man darauf achten sollte, dass man thematisch trennt, so dass man möglichst wenig Abhängigkeiten zwischen den Dateien hat und vor allem nur in eine Richtung. Ringabhängigkeiten sind zu vermeiden.
zu d) wo sind denn da globale Variablen? Bei POSSIBLE_MOVES sieht man doch schon an der Schreibweise, dass das eine Konstante ist.
zu d) wo sind denn da globale Variablen? Bei POSSIBLE_MOVES sieht man doch schon an der Schreibweise, dass das eine Konstante ist.
- __blackjack__
- User
- Beiträge: 13123
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Wobei das mit dem Hoch- und Runterscrollen bei modernen Editoren nicht wirklich eine Begründung ist. Wenn ein Editor keine Baumdarstllung der Struktur und/oder „folding“ beherrscht wo man alles bis auf die Klassen und Funktions- und Methodensignaturen ausblenden kann, sollte man sich nach einem anderen Editor beziehungsweise einer anderen IDE umschauen.
Und das Problem verschwindet doch auch überhaupt nicht wenn man jede Klasse in eine eigene Datei stecken‽ Dann muss man ja sogar die Datei wechseln wenn man Methoden in anderen Klassen sehen will, wenn die in anderen Dateien stecken.
Und das Problem verschwindet doch auch überhaupt nicht wenn man jede Klasse in eine eigene Datei stecken‽ Dann muss man ja sogar die Datei wechseln wenn man Methoden in anderen Klassen sehen will, wenn die in anderen Dateien stecken.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
@Sirius3:
zu b:
Das klingt sinnvoll. Was genau meinst Du mit Abhängigkeiten bzw. Ringabhängigkeiten? Ich kann mir leider nicht vorstellen wie das gemeint ist. Kannst Du mir ein Beispiel nennen oder einen Link schicken, damit mir das klarer wird?
zu d:
Ups, mein Fehler.
@__blackjack__:
Ich benutze PyCharm. Jetzt wo du es sagst, erscheinen mir die "Pluszeichen" links zum folden sehr sinnvoll Eine Baumdarstellung habe ich allerdings noch nicht entdeckt. Die finde ich aber sicherlich noch irgendwo
Danke auf jedenfall an alle die sich hier so viel Mühe geben
zu b:
Das klingt sinnvoll. Was genau meinst Du mit Abhängigkeiten bzw. Ringabhängigkeiten? Ich kann mir leider nicht vorstellen wie das gemeint ist. Kannst Du mir ein Beispiel nennen oder einen Link schicken, damit mir das klarer wird?
zu d:
Ups, mein Fehler.
@__blackjack__:
Ich benutze PyCharm. Jetzt wo du es sagst, erscheinen mir die "Pluszeichen" links zum folden sehr sinnvoll Eine Baumdarstellung habe ich allerdings noch nicht entdeckt. Die finde ich aber sicherlich noch irgendwo
Danke auf jedenfall an alle die sich hier so viel Mühe geben
@Domroon: Ein Ringabhängigkeit (circular dependency) ist, wenn zwei Module sich gegenseitig importieren. Das ist in Python problematisch, weil der Code von oben nach unten abgearbeitet wird. Beispiel:
Wenn `bar` importiert wird, wird `from foo import ANSWER` ausgeführt, aber `ANSWER` ist in `foo` noch gar nicht definiert worden.
Code: Alles auswählen
# foo.py
from bar import bar
ANSWER = 42
def foo():
print(bar())
Code: Alles auswählen
# bar.py
from foo import ANSWER
def bar():
return ANSWER
Code: Alles auswählen
# main.py
from foo import foo
if __name__ == "__main__":
foo()
Code: Alles auswählen
» python main.py
Traceback (most recent call last):
File "main.py", line 2, in <module>
from foo import foo
File "/tmp/t/foo.py", line 2, in <module>
from bar import bar
File "/tmp/t/bar.py", line 2, in <module>
from foo import ANSWER
ImportError: cannot import name 'ANSWER' from partially initialized module 'foo' (most likely due to a circular import) (/tmp/t/foo.py)
@narpfel: ah okay, danke für das Beispiel, ich habs verstanden
Hallo nochmal,
ich bin gerade dabei die User-Klasse zu erweitern.
Es soll nun möglich sein einem User ein Passwort zuzuweisen. Zunächst wird die passwort-länge überprüft bevor der eingegebene String dem passwort-attribut zugewiesen wird. Hier die wichtigen Code-stellen aus meinem Repository (https://github.com/Domroon/RockPaperScissor) :
Ich finde es unschön wie ich hier das passwort zuweise. In der Initialisierungsmethode weise ich dem passwort-attribut zunächst "None" zu da ich es nicht anders hinbekommen habe.
Meine Frage: Ist es möglich die passwort-setter-methode in der init-methode irgendwie aufzurufen, damit die user-klasse direkt immer mit "password" initialisiert wird? Gut, ich kann bei der initialisierungsmethode "password" mit übergeben aber dann wird dieses ja nicht durch meine password-setter-methode überprüft ob dies überhaupt ein gültiges passwort ist
Ich stecke in der Zwickmühle..
Danke euch schonmal im vorraus
ich bin gerade dabei die User-Klasse zu erweitern.
Es soll nun möglich sein einem User ein Passwort zuzuweisen. Zunächst wird die passwort-länge überprüft bevor der eingegebene String dem passwort-attribut zugewiesen wird. Hier die wichtigen Code-stellen aus meinem Repository (https://github.com/Domroon/RockPaperScissor) :
Code: Alles auswählen
PASSWORD_LENGTH = 8
class User:
def __init__(self, name):
self.score = 0
self.name = name
self._password = None
self._choice = "nothing"
def add_point(self):
self.score += 1
@property
def choice(self):
return self._choice
@choice.setter
def choice(self, move):
if move not in POSSIBLE_MOVES:
raise ValueError(f'You can choose between {" / ".join(POSSIBLE_MOVES)}')
self._choice = move
@property
def password(self):
return self._password
@password.setter
def password(self, password):
# password needs:
# letter in upper and lower case
# at least one special sign
# at least one number
if len(password) >= 8:
self._password = password
else:
raise ValueError("Password needs at least 8 character")
def test():
user = User("Max")
try:
user.password = "asdf"
except ValueError as err:
print(err)
if user.password is None:
print("User password is incorrect")
else:
print("You can use this password")
def main():
test()
'''
print('Welcome to the Game "Rock, Paper or Scissor!"')
name = input("Name: ")
print(f"Hello {name}! Lets start the Game!")
user = User(name)
computer = Computer()
game = Game(user, computer)
while user.score < MAX_SCORE and computer.score < MAX_SCORE:
game.round()
print("Thank you for gaming!")
'''
if __name__ == '__main__':
main()
Meine Frage: Ist es möglich die passwort-setter-methode in der init-methode irgendwie aufzurufen, damit die user-klasse direkt immer mit "password" initialisiert wird? Gut, ich kann bei der initialisierungsmethode "password" mit übergeben aber dann wird dieses ja nicht durch meine password-setter-methode überprüft ob dies überhaupt ein gültiges passwort ist
Ich stecke in der Zwickmühle..
Danke euch schonmal im vorraus
- __blackjack__
- User
- Beiträge: 13123
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Domroon: Zeichenketten sind keine Kommentare, die sollte man nicht zum auskommentieren von Code verwenden. Das Kommentarzeichen in Python ist #, und nur das.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman