ein bisschen Spaß an einer Tennis-Liga-Simulation

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten

Wie lange würdet ihr für dieses Programm benötigen?

mit verbundenden Augen in der Kaffeepause
1
14%
1-2 Wochen
2
29%
3-4 Wochen
1
14%
1-2 Monate
0
Keine Stimmen
2-4 Monate
1
14%
4-6 Monate oder noch länger
2
29%
 
Insgesamt abgegebene Stimmen: 7
matbo
User
Beiträge: 4
Registriert: Freitag 12. April 2013, 16:46

hallo,

wer mag, kann sich ja mal mein Lernprojekt anschauen.
Es handelt sich um eine Tennis-Liga-Simulation.
Es gibt genau eine Interaktion und zwar während eines Matches.
Ansonsten kann man einfach nur ein bisschen beobachten, wer Meister wird, absteigt und aufsteigt
und vielleicht auf seinen eigenen Spieler Wetten abschließen....

https://gist.github.com/matbo81/5372975

es handelt sich um 4 python-Dateien und 3 csv-Dateien.

die Ziele meines Lernprojektes:
-einfache Einbindung einer Datenbankdatei csv
-flexibles und einfach zu erweiterndes Textmenü
-das Arbeiten mit Objekten
-Spaß haben

:D
viel Spaß
matbo
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo matbo,
in Python braucht man keine Getter und Setter und sollte sie der Leserlichkeit wegen auch nicht benutzen. Das Gegenteil (property) kann manchmal nützlich sein.
String sollte man nicht mit + und »str« zusammensetzen. Du hast ja schon gezeigt, dass Du »format« kennst, aber Du solltest auch kein »str« innerhalb »format« benutzen, das ist unnötig.
»PlayerDict.__str__« liefert keinen String zurück.
Du benutzt an seltsamen Stellen Klammern.
matbo
User
Beiträge: 4
Registriert: Freitag 12. April 2013, 16:46

hallo Sirius3,

danke für dein Feedback.
Das man Getter und Setter weglassen sollte, habe ich erst im Laufe des Projektes gelernt. In späteren Codezeilen greife ich dann direkt auf die Attribute zu.
property nehme ich mir mal vor.
BlackJack

@matbo: Es ist ja nicht so als wenn man einmal geschriebene Getter/Setter nicht auch wieder entfernen könnte. ;-)
matbo
User
Beiträge: 4
Registriert: Freitag 12. April 2013, 16:46

...das habe ich nicht mehr gemacht...."opps!" ;)
ich war mir nie ganz sicher, ob das okay ist und jetzt bin ich an anderen Dingen dran.
BlackJack

@matbo: Erster und IMHO sehr wichtiger Kritikpunkt: Es läuft nicht. Du versuchst im `start`-Modul ein `Menu`-Modul zu importieren dass es nicht gibt:

Code: Alles auswählen

$ python start.py
Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import Menu 
ImportError: No module named Menu
Namen in Python sind „case sensitive”, dass heisst Gross- und Kleinschreibung spielt eine Rolle.

Das `start`-Modul besteht nur aus diesem einen Import und sonst nichts. Damit ist es ziemlich überflüssig. Ausserdem bedeutet es, dass man `menu` nicht importieren kann, ohne dass das Programm abläuft. Module sollte man aber so schreiben, dass man sie ohne Effekte importieren kann. Denn nur dann kann man sie wiederverwenden, einzelne Funktionen oder Klassen (automatisiert) testen, damit „live” in einer Python-Shell herum spielen um sie zu erkunden oder Fehlern interaktiv auf die Spur kommen, und so weiter.

Bei den Kommentaren wird zu viel gebrauch von '#' gemacht. Eines am Anfang der Kommentarzeile reicht völlig aus. Wozu das anhübschen mit vielen '#' führen kann, sieht man an einigen Kommentaren wo der Text anscheinend nachträglich verändert wurde und jetzt diese Kommentarblockrahmen rechts nicht mehr einheitlich abschliessen.

Im `menu`-Modul ist in Zeile 16 eine Zeichenkette die keinen Effekt hat. Wenn das ein Kommentar sein soll, der an *der* Stelle im Quelltext stehen soll, dann bitte wirklich als Kommentar und nicht als Zeichenkette. Falls es der DocString für das Modul sein soll, dann steht er syntaktisch an der falschen Stelle und gehört vor die ``import``-Anweisungen oben im Modul. Vom Inhalt her scheint mir das aber weder das Modul zu beschreiben noch irgend etwas mit den Funktionen zu tun zu haben, die darunter definiert werden‽

Die Namensgebung und die Quelltextformatierung halten sich nicht an den Style Guide for Python Code.

Die Namen sind auch teilweise ziemlich kryptisch. Wenn Du `menu_width` meinst, dann schreib das doch auch statt `mw` unter dem man sich nichts vorstellen kann. Diese Funktion aus dem `Texte`-Modul gibt nur eine Zahl zurück, also eigentlich hätte eine Konstante völlig ausgereicht, statt das man in allen möglichen Funktionen und Methoden `Texte.mw()` aufruft und den Wert dann lokal noch mal an einen Namen bindet. Man hätte das auch besser in einer Klasse gekapselt die Methoden hat um Texte zentriert und mit ”Verzierungen” wie Linien und so weiter auszugeben.

Die erste „Abkürzung” in `menuOptions()` verstehe ich nicht. Warum wird da die Funktion verlassen? So etwas gehört dokumentiert.

Einen Namen sollte man nur für den selben („duck”-)Typ verwenden und nicht wie das `special`-Argument für `False` *oder* für eine Liste (oder allgemein ein iterierbares Objekt). Dann ist der Quelltext leichter verständlich, weniger fehleranfällig, und kann auch vereinfacht werden. Wenn man dort statt `False` einfach eine leere Liste übergeben würde, kann man sich das ``if`` sparen und dieses Argument einfach *immer* als Liste/iterierbares Objekt behandeln. Der Name `special` sagt irgendwie nicht wirklich aus, dass es sich um optionale zusätzliche Kopfinformation für das Menü handelt. Da wäre `header_lines` zum Beispiel treffender, und da es optional ist, sollte man das Argument nach hinten in der Signatur verschieben und als Defaultwert zum Beispiel ein leeres Tupel angeben.

``for i in range(len(sequence)):`` um dann mit der Laufvariablen auf eine oder mehrere Sequenzen per Index zuzugreifen ist in Python ein „anti pattern”. Man kann *direkt* über die Elemente von Sequenzen iterieren, ohne einen Umweg über einen Index. Wenn man *zusätzlich* eine fortlaufende Zahl benötigt, gibt es die `enumerate()`-Funktion. Die man auch bei anderen Startwerten als 0 anfangen lassen kann. Soll „parallel” über mehrere Sequenzen iteriert werden, gibt es die `zip()`-Funktion.

Wenn man über „parallele” Listen itieriert, sollte man sich auch fragen warum das überhaupt nötig ist, denn wenn dabei zusammengehörende Daten in verschiedenen Listen stecken, hat man sehr wahrscheinlich etwas bei der Datenstruktur falsch gemacht.

Der Aufruf von `Texte.turnInfo()` hat in der `menuOptions()`-Funktion IMHO nichts zu suchen, denn dadurch ist das Menü nicht mehr allgemein verwendbar.

Auch in der `menuInput()` verstehe ich den Sinn der ersten beiden Zeilen nicht. Genau so wenig wie die `Texte.hitWords()`-Funktion. Die könnte glatt aus einer Anleitung für möglichst schlechten, verwirrenden Quelltext stammen. Der Name der Funktion ist mir unklar, und einen verständlichen Text mit einem kryptischen Funktionsaufruf mit einer magischen Zahl zu verschleiern ist echt schräg.

Du hast auch an mehreren Stellen ``while``-Schleifen bei denen Namen erst an Dummy-Werte gebunden werden, die erst innerhalb der Schleife an sinnvolle Werte gebunden werden. Das sind eigentlich Schleifen bei denen die Abbruchbedingung *in* der Schleife beziehungsweise oft an deren Ende geprüft werden. Dafür verwendet man in Python eine „Endlos-Schleife” und die ``break``-Anweisung. Beispiel:

Code: Alles auswählen

# 
# anstatt:
# 
spam = None
while spam != 'end':
    spam = do_something()
# 
# besser:
# 
while True:
    spam = do_something()
    if spam == 'end':
        break
Die `menulist` bei der `__init__` der `Menu`-Klasse ist viel zu kompliziert. Da sollten einzelne Argumente übergeben werden statt einer komplexen Datenstruktur. Wenn man so etwas übergeben können möchte, dann stellt man dafür üblicherweise eine Klassenmethode (`classmethod()`) zusätzlich zur Verfügung.

`execs` klingt nach Mehrzahl, es wird dort daran aber nur ein aufrufbares Objekt gebunden — das ist irreführend. Ausserdem gibt es dort auch wieder das Problem, dass entweder ein aufrufbares Objekt *oder* `False` gebunden wird, was an jeder Stelle wo man das Attribut verwenden möchte, einen Test erforderlich macht. Der übliche Wert wenn man „nichts” meint ist nicht `False` sondern `None` und wenn `None` übergeben wurde, sollte in der `__init__` dafür gesorgt werden, dass an das Attribut *in jedem Fall* ein aufrufbares Objekt gebunden wird, damit man beim Verwenden nichts mehr prüfen muss.

Die `update_*`-Methoden müssten eigentlich `set_*` heissen, wenn triviale Getter/Setter in Python nicht sowieso unüblich wären.

Die `make*`-Methoden „machen” nichts in dem Sinne in dem man im englischen das Wort „make” verwendet und auch wenn das deutsche „mache dieses oder jenes” gemeint wäre, dann ist das einfach nur ein allgemeines Füllwort welches man durch eine treffenderes ersetzen sollte, was besser beschreibt was diese Methoden leisten. Beispielsweise `print_options()` und `get_input()`. Da beide Methoden nur den Aufruf von Funktionen enthalten, stellt sich die Frage warum diese Funktionen nicht stattdessen an dieser Stelle stehen.

Diese ``# end-irgendwas``-Kommentare sind eigenartig. Wenn Du Python's Syntax ohne „druckbare” Blockenden nicht magst, dann verwende besser eine andere Programmiersprache. Das Ende sollte man auch ohne solche Kommentare erkennen können.

`MenuController` verwendet in der `__init__` Daten die auf „magische Weise” aus einem anderen Modul genommen werden, statt als Argument übergeben zu werden. Damit ist das `Texte`-Modul viel zu eng an das `menu`-Modul gekoppelt. Das war es bis zu diesem Zeitpunkt zwar auch schon, zum Beispiel durch die `mw()`-Funktion, aber hier ist ein Punkt erreicht der das `menu`-Modul nicht für andere Aufgaben wiederverwendbar macht.

Dabei wird auch wieder eine magische, nichtssagende Zahl verwendet, denn was soll die 2 in ``Texte.menusDict(2)`` aussagen? Was wäre hier die Bedeutung von 0, 1, 3, …?

Was bedeutet „Chall” in `ChallView`?

Das was danach mit den magischen Zahlen mit `self.titles` passiert ist gruselig. Da werden total unnötige Indirektionen über magische Zahlen/Indexe(?) erzeugt, statt eine vernünftige Datenstruktur zu verwenden, wo alle Informationen die zusammengehören auch zusammengefasst gespeichert werden.

Die auskommentierten Debug-Ausgaben kann man eleganter mit dem `logging`-Modul umsetzen. Dann braucht man sie nämlich nicht auskommentieren und kann sie trotzdem je nach Bedarf (de)aktivieren.

In `last_check()` werden Sachen gemacht die nicht objektorientiert sind. Statt in einer riesigen Methode zu testen welchen Menüpunkt man vor sich hat und dann entsprechend etwas zu tun, sollte man einfach eine Methode auf dem gewählten Menüpunkt-Objekt — ein Typ den es in diesem Programm noch nicht gibt — aufrufen, der dann die Aktion selbst durchführt oder aber eine ihm dafür übergebene Funktion oder Methode aufruft.

So, das war's erst einmal für dieses eine Modul. Ein kurzer Blick in das `Texte`-Modul lässt Böses erahnen was die Verwendung von globalen Daten(strukturen) angeht. Da ist sicher noch viel zu tun.

Die (Um)Frage nach der Programmierdauer kann ich nicht beantworten, weil ich es nicht zum Laufen bekomme und damit auch nicht so einfach sehen kann was das Programm eigentlich leistet. Wenn ich den Namen der Menu-Datei korrigiere kommt bei mir das hier:

Code: Alles auswählen

$ python3 start.py 
Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import Menu 
  File "/home/bj/tmp/affsaison01/Menu.py", line 1, in <module>
    from AffSaison import game
  File "/home/bj/tmp/affsaison01/AffSaison.py", line 867, in <module>
    game = Game()
  File "/home/bj/tmp/affsaison01/AffSaison.py", line 752, in __init__
    self.init_saison()
  File "/home/bj/tmp/affsaison01/AffSaison.py", line 787, in init_saison
    liga.add_turnier(name, punkte, time, self)
  File "/home/bj/tmp/affsaison01/AffSaison.py", line 509, in add_turnier
    self.playersToTurnier(tName)
  File "/home/bj/tmp/affsaison01/AffSaison.py", line 512, in playersToTurnier
    self.turnierListe[tu].set_players(self.standings)
  File "/home/bj/tmp/affsaison01/AffSaison.py", line 367, in set_players
    self.players = new.copy()
AttributeError: 'list' object has no attribute 'copy'
Benutzeravatar
kevind
User
Beiträge: 71
Registriert: Montag 22. Oktober 2012, 20:23
Wohnort: /dev/null

Ist man in anderen Programmiersprachen auf "getter/setter" angewiesen ? Oder welchen Sinn haben die eigentlich ?

Gruss, Kev
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

kevind hat geschrieben:Ist man in anderen Programmiersprachen auf "getter/setter" angewiesen ? Oder welchen Sinn haben die eigentlich ?
Nicht angewiesen, aber man gewinnt damit Freiheiten beim API-Design, da man einfach Code zu den Gettern/Settern hinzufuegen kann, ohne dass sich fuer den Nutzer etwas aendert. Bei Gettern z.B. Logging, bei Settern z.B. Validierung. In Python kommt man wegen Properties ohne diesen Umweg aus.
Benutzeravatar
kevind
User
Beiträge: 71
Registriert: Montag 22. Oktober 2012, 20:23
Wohnort: /dev/null

Aber hier werden doch sowieso nur Klassen-attribute zurückgegeben ? Verstehe jetzt denn sinn noch net.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Getter- und Setter-Funktionen verwendet man beispielsweise in Java (sehr exzessiv), C++ und anderen Sprachen, welche keine Properties kennen. In diesen vermeidet man öffentliche Member gänzlich und deklariert diese privat (= außerhalb der Klasse nicht ansprechbar). Um dennoch auf diese zugreifen zu können, wer die vorgenannten Funktionen verwendet.

Python kennt jedoch keine privaten und öffentlichen Attribute und es hat properties. Daher ist es auch nicht sinnvoll und nötig, Getter und Setter zu verwenden.

Grüße ... bwbg
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

kevind hat geschrieben:Aber hier werden doch sowieso nur Klassen-attribute zurückgegeben ? Verstehe jetzt denn sinn noch net.
Ebend ;)
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Benutzeravatar
kevind
User
Beiträge: 71
Registriert: Montag 22. Oktober 2012, 20:23
Wohnort: /dev/null

Okay jetzt kann ich mir das vorstellen. Danke
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

kevind hat geschrieben:Aber hier werden doch sowieso nur Klassen-attribute zurückgegeben ?
Aus Präzisionsgründen: Das sind keine Klassenattribute. Das sind Attribute eines Exemplars (einer Instanz) der Klasse.
matbo
User
Beiträge: 4
Registriert: Freitag 12. April 2013, 16:46

@BlackJack
danke BlackJack für die vielen Anmerkungen.
Das ist einer der Gründe warum ich diesen "Programmschlamassel" mal hier gepostet habe, der aber immerhin bei mir funktioniert.
Gut, den Menudateinamen habe ich jetzt groß geschrieben, damit dürfte der Fehler weg sein.
deine letzte Fehlermeldung ('list' object has no attribute 'copy') habe ich auch einmal erhalten, als ich das Programm probehalber mit python 2.7 gestartet habe. Ansonsten in der aktuellen 3.3.1 Version läuft es zumindest bei mir. Schade, dafür.

Das "Texte"-Modul ist sicher noch schlimmer, es ist ein Ansammlung an Hilfe-Classes und -Funktionen.

Ich denke, man sieht ganz klar, dass ich das Programm ohne vorher angefertigtes Game Design und mit viel Eigeninterpretationen und Viertelwissen von Konzepten geschrieben habe.

Aber deine Punkte werde ich mir zu Herzen, z.B. die undeutlichen Bezeichnungen, die Vermeidung von Iterationen mit Laufvariablen, das "logging" Modul, auf Mehrverwendbarkeit von Modulen zu achten und, und, und,...

so viel Arbeit hast du dir beim Analysieren gegeben und dann kamst du nicht mal in den Genuss des Spiels... ;)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

bwbg hat geschrieben:Getter- und Setter-Funktionen verwendet man beispielsweise in Java (sehr exzessiv), C++ und anderen Sprachen, welche keine Properties kennen. In diesen vermeidet man öffentliche Member gänzlich und deklariert diese privat (= außerhalb der Klasse nicht ansprechbar). Um dennoch auf diese zugreifen zu können, wer die vorgenannten Funktionen verwendet.
Wobei AFAIK Alan Kay von dieser Praxis nicht so begeistert ist, weil man damit letztendlich die privaten Attribute doch wieder publiziert.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
trvhgoy
User
Beiträge: 2
Registriert: Mittwoch 17. April 2013, 11:16

Leonidas hat geschrieben:Wobei AFAIK Alan Kay von dieser Praxis nicht so begeistert ist, weil man damit letztendlich die privaten Attribute doch wieder publiziert.
Wenn man einen Getter für eine Instanzvariable schreibt will man diese ja bewusst publizieren, daher verstehe ich diesen Einwand nicht so ganz. Für eine Instanzvariable die ich nicht publizieren will schreibe ich ja auch keinen Getter.
Das gleiche gilt für Setter.

Der größte Vorteil m. E. wenn man bei anderen Sprachen (wie Java) Getter und Setter verwendet ist, dass man z. B. eine Berechtigungsprüfung (Getter) oder eine Datenvalidierung (Setter) "nachrüsten" kann ohne, dass der Benutzer der Klasse alle Verwendungsstellen anpassen muss.
BlackJack

@trvhgoy: Die Argumentation gegen triviale Getter *und* Setter ist, dass man dadurch internen Zustand nach aussen gibt. Man kann also den Wert abfragen, verändern, und wieder zurück schreiben, wo Kay eher eine Methode auf dem Objekt erwarten würde, welche den internen Zustand verändert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Der Einwand von Kay war, dass man Klassen über ihre Oparationen modifizieren soll, also ``konto1.überweise(konto2)`` statt ``konto1.set_kontostand(konto1.get_kontostand() - X); konto2.set_kontostand(konto2.get_kontostand() + X)`` und nicht über ihre Variablen arbeiten soll.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Das würde ja auch dem Sag-was-Du-willst-und-nicht-wie-es-gemacht-werden-soll-Prinzip. Auf Englisch war das glaube ich kürzer, aber ich komm grad nicht drauf.
trvhgoy
User
Beiträge: 2
Registriert: Mittwoch 17. April 2013, 11:16

BlackJack hat geschrieben:@trvhgoy: Die Argumentation gegen triviale Getter *und* Setter ist, dass man dadurch internen Zustand nach aussen gibt. Man kann also den Wert abfragen, verändern, und wieder zurück schreiben, wo Kay eher eine Methode auf dem Objekt erwarten würde, welche den internen Zustand verändert.
Leonidas hat geschrieben:Der Einwand von Kay war, dass man Klassen über ihre Oparationen modifizieren soll, also ``konto1.überweise(konto2)`` statt ``konto1.set_kontostand(konto1.get_kontostand() - X); konto2.set_kontostand(konto2.get_kontostand() + X)`` und nicht über ihre Variablen arbeiten soll.
Aso, so war das gemeint. Da habt ihr / Alan Kay natürlich recht.
Diese Verwendung von Gettern / Settern entspricht nicht dem Gedanken der hinter OOP steht.
Antworten