Generelles Vorgehen beim Programmieren

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
CasualCoding.org

Hallo Forum!

Ich bin in meinem Privatleben begeisterter Linux-Nutzer und interessiere mich darüber hinaus sehr fürs Programmieren. Es macht mir wirklich viel Spaß, und ich habe so einige Projektideen im Hinterkopf, die ich gern mal irgendwann umsetzen würde. Aber ich stoße immer wieder auf dasselbe, für mich wirklich große Problem. Ich habe eine zeitlang sehr intensiv Java gelernt, ich habe mich in den letzten Jahren immer und immer wieder mal mit C und C++ beschäftigt, und jetzt gerade arbeite ich zum zweiten Mal das Python-Buch von Michael Weigend durch.

Mein Problem ist nicht das Erlernen der Syntax und der Konzepte einer Sprache an sich. Ich habe wenige Verständnisprobleme bei den einzelnen Kapiteln meiner Lehrbücher, ich verstehe die ganzen Codebeispiele, ich weiß, warum die Autoren wann was machen, und auch die Übungsaufgaben bereiten mir meist wenig Probleme.

Woran es dann aber regelmäßig hängt, ist die Umsetzung in eigene, reale Projektideen. Das Zusammenfügen der einzelnen Aspekte zu einem Ganzen. Ich kann nicht einmal genau beschreiben, wo konkret mein Problem liegt, sondern ich stehe einfach vor einem großen Haufen offener Fragen und weiß nicht, wo ich ansetzen soll. Und zwischen den Einstiegs-Lehrbüchern mit ihren Minibeispielen, sowie den Mini-Übungsaufgaben auf den einschlägigen Webseiten einerseits und dem Studieren von realem Code andererseits liegt für mich eine Lücke, die ich einfach nicht überwunden bekomme.

Ich bin diesbezüglich wirklich ratlos. Helfen Bücher wie die eher theoretischen der Head-First-Reihe ("Softwareentwicklung", "Programmierung", etc.)? Ist es sinnvoll, einfach irgendwas zusammenzuschustern, egal wie grauenhaft und unübersichtlich es ist, und es hier auseinander nehmen zu lassen?

Ich weiß wirklich nicht, wie ich an diesem entscheidenden Punkt weiterkommen soll und wäre für Tipps sehr dankbar.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Du hast anscheinend Probleme, die nötige Struktur in dein Denken reinzubekommen. Das kann man IMHO nur durch viel Übung mittels praktischen Umsetzungen (also Code-Schreiben) erlernen. Du kannst gerne deinen "grauenhaften" Code zur Beurteilung ins Forum stellen. Hier finden sich eigentlich immer ein paar Leute, die sich die Zeit nehmen, gepostete Schnipsel durchzugehen und zu kommentieren.

Achso, und dir fehlt wahrscheinlich auch generell die Routine, um zu wissen, wie man eine Aufgabe am Besten mit den Möglichkeiten der Sprache umsetzen kann. Auch hier gilt wieder: Learning by Doing.
CasualCoding.org

Erstmal danke für deine Antwort. Aber ja, das ist der Standard-Tipp, den ich auch vollkommen verstehe; nur weiß ich eben gar nicht, wo ich ansetzen soll.

Machen wir es konkret: Eines meiner größten Wunschprojekte ist die Umsetzung eines Würfelspiels, das ich aus meinen Barkeeper-Tagen kenne. Ich habe da für den Anfang gar keine großen Visionen. Es muss erstmal keine GUI haben und auch keine tausend Einstellungen und Optionen. Einfach erstmal den grundsätzlichen Spielablauf für einen menschlichen Spieler gegen, sagen wir, drei Computerspieler auf der Konsole.

Eine schöne Spielerklasse bekomme ich wahrscheinlich noch hin, also nicht nur irgendwie, sondern sogar halbwegs akzeptabel und für die jeweilige Sprache einigermaßen idiomatisch. Aber dann sitze ich vor meiner Spielerklasse und weiß nicht, wie ich von da aus weitermachen soll. Da sind dann einfach nur Fragezeichen.

Nehmen wir ein ganz einfaches Würfelspiel. Wie wäre denn deine Herangehensweise an sowas?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

SolitaryMan hat geschrieben: Nehmen wir ein ganz einfaches Würfelspiel. Wie wäre denn deine Herangehensweise an sowas?
Naja, Du hast bei einem Spiel ja Spielregeln - bei jedem Gesellschaftsspiel, das man kauft, ist doch letztlich eine Beschreibung des Ablaufs und der Regeln dabei. Das ist letztlich in Prosa ein Algorithmus für das Spiel. Diese Angaben muss man nun eben mit den Mitteln einer Programmiersprache umsetzen.

Du musst Dir eben überlegen, was für Objekte Du benötigst und wie diese interagieren. Dann kommt da noch ein Ablauf rein, also idR. eine Schleife, in der so lange verweilt wird, bis das Spiel zu Ende ist (also vermutlich eine Siegbedingung eingetroffen ist o.ä.).

Wo genau liegt da jetzt eine konkrete Schwierigkeit? Man kann hier nur allgemein bleiben, weil das eben die Essenz des Programmierens ist - genau dieses Vorgehen macht den kreativen Prozess beim Entwickeln aus. Das kann man nur lernen, indem man sich selber daran versucht und die Gedankengänge und Vorgehensweisen anderer analysiert und nachvollzieht.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

SolitaryMan hat geschrieben:Eine schöne Spielerklasse bekomme ich wahrscheinlich noch hin, also nicht nur irgendwie, sondern sogar halbwegs akzeptabel und für die jeweilige Sprache einigermaßen idiomatisch. Aber dann sitze ich vor meiner Spielerklasse und weiß nicht, wie ich von da aus weitermachen soll. Da sind dann einfach nur Fragezeichen.

Nehmen wir ein ganz einfaches Würfelspiel. Wie wäre denn deine Herangehensweise an sowas?
Naja, Objekte haben Eigenschaften und Fähigkeiten. Konkret gesagt könnte eine Eigenschaft von Würfelspielern sein, dass sie einen Punktestand haben. Eine Fähigkeit wäre das Würfeln: `Player.throw_dice()` (oder sowas ähnliches). Eine Art Spielverwaltung (als eigene Klasse) weiß, wer aktuell an der Reihe ist und ruft die Würfel-Methode des passenden Spielers auf. Auch könnte man eine Art Regelprüfer implementieren, der darauf achtet, dass ein Spieler z.B. nur 3 mal würfeln darf. Bei Verstößen wird eine Ausnahme (etwa: `IllegalActionError`) geworfen, usw. Die zuvor genannte Spielverwaltung (`Game`) könnte jetzt mit einem bestimmten Regelprüfer und einer Liste von Spieler-Objekten initialisiert werden. Mittels `game.play()` würde eine Schleife gestartet werden, die den Spielbeginn einleitet. Es gäbe für den Anfang sicher nur einen Regelprüfer (`Rules`), aber vielleicht möchte man später weitere Regel-Klassen implementieren, um Abwandlungen spielen zu können. Auch Spieler könnten gewisse gemeinsame Funktionalität in einer Basisklasse haben und dann in abgeleiteter Form in menschliche oder computergesteuerte Spieler unterteilt werden. Die Computerspieler-Intelligenz wiederum wäre auch als eigene Klasse mit Raum für Erweiterungen (vielleicht sogar als Plugins) denkbar.

Also mir fällt hier jede Menge ein, was man theoretisch machen könnte. Wichtig ist es aber, zunächst einmal mit eher simpel gehaltenen Strukturen anzufangen, damit man sich nicht völlig in der Planung verliert. Später kann ja bei Bedarf immer noch Refactoring des Codes betrieben werden. Einiges will man vielleicht auch erstmal als simple Option/Flag einbauen (z.B. `Player.__init__(self, human=True)`). Wichtig ist, dass irgendwann etwas da ist, was man tatsächlich starten kann. Und danach kann halt beliebig weitergemacht werden. Je nach persönlicher Motivation und Zeit natürlich.

Da du sagstest, dass du besonders Java intensiver gelernt hast, denke ich mal, dass ich dir teilweise nichts wirklich Neues erzählt habe. Allerdings lässt sich deine Fragestellung anders auch nicht beantworten.
CasualCoding.org

snafu hat geschrieben:Da du sagstest, dass du besonders Java intensiver gelernt hast, denke ich mal, dass ich dir teilweise nichts wirklich Neues erzählt habe. Allerdings lässt sich deine Fragestellung anders auch nicht beantworten.
Naja, doch, ein Stückchen hast du mir schon weiter geholfen. Die Idee zu einer Spielverwaltungsklasse hätte ich zum Beispiel auch gehabt - aber das Zusammenfügen der Einzelteile, das wäre jetzt schon wieder mein Problem gewesen. Hier liegen vielleicht einfach auch Defizite vor, was das Verständnis von Objektorientierung angeht - eigentlich ist deine geschilderte Idee, einer Spielverwaltungsinstanz Spielerobjekte und ein Regelüberwachungsobjekt als Argumente zu übergeben und in einer Schleife ihre Arbeit tun zu lassen, nämlich sehr simpel.

Aber wenn ich vor meinen Projektideen sitze und meinen Klassencode vor mir habe, bin ich urplötzlich ratlos und völlig blockiert für sowas. Noch schlimmer wird es dann, wenn eine GUI und damit eine zusätzliche "Schicht" dazu kommt. Ein paar kleinere Sachen hatte ich in Java durchaus hinbekommen, aber danach eine Swing-GUI davor zu setzen, ging dann schon wieder komplett schief.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Und wie gesagt: Nimm dir nicht zuviel auf einmal vor. Man könnte ja erstmal ein Spiel mit Phantasie-Regeln schreiben, wo es nur darum geht, mit 3 Würfeln mindestens 10 Punkte zu erzielen, wobei für den Anfang beliebig oft wiederholt werden darf, bis eben ein Wurf das Minimum überschritten hat. Die Augenzahl wird dann aufgeschrieben und der nächste Spieler ist dran. Wer als erstes insgesamt 100 Punkte hat, ist der Sieger. Sowas halt. Die Konzentration läge da also auf der Ermittlung einer zufälligen Augenzahl je Würfel (das Modul `random` ist dein Freund), sowie der allgemeinen Spielverwaltung. Für den Anfang könnte die Spiellogik (sprich: die Regeln) ja noch in der `Game`-Klasse implementiert sein und die Spielerstände wären als Dictionary denkbar. Auch die Fehlerbehandlung könnte für den ersten Prototypen erstmal außen vorgelassen werden. Und wenn das alles steht, möchtest du es vielleicht hier präsentieren und dann machste weiter, damit der Spielverlauf komplexer wird und Verbesserungsvorschläge einfließen können.
mcdwerner
User
Beiträge: 113
Registriert: Donnerstag 7. Juli 2011, 14:27

@ SolitaryMan:

hier: http://www.udacity.com/view#Course/cs21 ... get/294001 wird der Entwurf eines Würfel-Spiels recht kleinschrittig und anschaulich gezeigt.

bin mittlerweile ein großer Fan von Udacity geworden...
schnickalot
User
Beiträge: 22
Registriert: Dienstag 13. August 2019, 14:38

Hallo zusammen,

der Thread ist ja schon einiges her, aber TE's Denk-Probleme kommen mir sehr bekannt vor. Ich bin auch ein Typ der 2-3 Sprachen per Anfänger Bücher durch hat, aber die Praxis ist dann doch "anders".. besonders mit eigenen Programmwünschen (Ich kann fib() nicht mehr sehen!).

Ich habe vor, einen Snooker Scorer zu schreiben. Speziell für Liga-Spieltage. Sinn ist eine Art Anzeigetafel im Billardclub zu haben wo die einzelnen Partien mitgescored werden.
Kurze Skizze des Hauptfensters:

Match Home Score Away
1 Klaus 0 (7) 0 Martin
2 Hans 0 (7) 0 Friedrich
3 Mike 0 (7) 0 Horst
4 Norbert 0 (7) 0 Andreas
5 Falk 0 (7) 0 Thomas
6 Stefan 0 (7) 0 Christian

Gespielt werden 6 Matches je best-of-7-Frames (1 Frame ist wie ein Spiel). Immer wenn ein Spieler ein Frame gewinnt, soll zu seinem Score +1 gerechnet werden. Eine Match ist vorbei wenn ein Spieler 4 Frames gewonnen hat. Die ganze Partie ist vorbei, wenn alle 6 Matches fertig sind (kann am Ende des Tages also auch 3-3 ausgehen).

Folgende Klassen habe ich erstellt:
Player (Attribute "Firstname", "Lastname", "Fullname", "Teamname", "Score")
Playerlist (erbt von Dict und speichert alle Playerinstanzen (Dict-Key = Fullname), welche auch per pickle in eine Datei gesichert und bei Programmstart geladen werden)

Team (Attribute "Name" und [Roster] (für die Aufstellung)) -> in der Liste "Roster" will ich alle Playerobjekte einer Mannschaft zwischenlagern, die zum Spieltag erscheinen und aufgestellt werden. Diese werden dann aus der kompletten Playerlist rausgepickt.
Teamlist (erbt von Dict und speichert alle Teaminstanzen (Dict-Key = Name), welche auch per pickle in eine Datei gesichert und bei Programmstart geladen werden)

-> Um Player und Teams zu managen habe ich bereits GUI-Screens fertig und das anlegen, editieren und löschen funktioniert prima. (Screens, weil ich auf Enigma2-TV-Boxen Python lerne)

Weitere Klassen:
Game (Attribute "Totalframes", "Winframe" (Totalframes // 2 + 1), "Hometeam", "Awayteam", [Matches]) -> Hometeam, Awayteam werden mit Teamobjekten belegt. In [Matches] sollen alle 6 Matchinstanzen liegen.

Match (Attribute "Homeplayer", "Awayplayer") -> Homeplayer, Awayplayer werden mit Playerobjekten belegt, die aus der Liste "Roster" nach einem festen Schema rausgepickt werden (Gespielt wird über Kreuz, also 1 gegen 2, 2 gegen 3, 3 gegen 1 usw.)

Ich hoffe es ist einigermassen verständlich. Jetzt zu meiner eigentlichen Frage:
Geht man so mit Klasseninstanzen um? zB die Playerinstanzen: Beim erstellen landen sie in der Playerlist und werden weggespeichert. Beim Spieltag werden sie dann in die Liste "Roster" eines Teams gesteckt, um sie danach einem Match-Attribut zuzuweisen. Dort wird der Score gepflegt und am Ende will ich eigentlich noch Statistiken speichern, wie zB "gespielte Matches", "gewonnene Matches, "verlorene Matches". Die Playerinstanz muss dann also auch wieder zurück in die Playerlist und gespeichert werden, damit die Statistiken nicht verloren gehen. Der laufende Score fängt jedes mal bei Null an und ist natürlich nicht wichtig zu speichern.
Oder lässt man Instanzen in der Liste (in meinem Fall die Playerlist) und spricht sie nur da an, ohne sie anderen Attributen oder Listen zuzuweisen?
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@schnickalot: Bei den Attributnamen hälst Du Dich anscheinend nicht an die Namenskonventionen. klein_mit_unterstrichen für alles ausser Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Was ist `Player.full_name`? Ist das wirklich eigenständig oder kann man das aus `Player.firstname` und `Player.surname` ”berechnen”? Dann wär's eher ein `property()` statt eines normalen Attributs.

`Playerlist`: Grunddatentypen haben nichts in Namen zu suchen. Und schon mal gar nicht wenn das dann auch noch von einem ganz anderen Grunddatentyp erbt. Wobei man von Grunddatentypen auch eher nicht erbt, weil die ganzen Methoden die man sich damit einfängt in der Regel gar nicht alle sinnvoll auf den neuen Datentyp anwendbar sind. Was hat denn Dein `Playerlist` denn an anderem Verhalten als ein `dict`? Brauchst Du da überhaupt einen eigenen Datentyp für?

`pickle` ist für längerfristige Datenspeicherung ungeeignet, weil das nur schlecht bis gar nicht mit Änderungen am Code klar kommt die Einfluss auf die Codeaufteilung und die Namensgebung haben. Zudem ist das auf Python beschränkt. Daten serialisiert man besser explizit in einem Standardformat. JSON bietet sich oft an.

Für `Teamlist` gilt das gleiche.

Die eigentliche Frage am Ende verstehe ich nicht so ganz beziehungsweise scheint die so ein bisschen auf ein Missverständnis auf Deiner Seite hinzudeuten. Eine `Player`-Objekt das Du aus `Playerlist` holst und dann dem `roster` von einem `Team` hinzufügst muss nicht „zurück in die Playerlist“, ausser Du entfernst das aus der `Playerlist` tatsächlich – wo ich keinen Grund für sehe das zu machen.

Man lässt die `Player` sowohl in der `Playerlist` *und* man weist sie anderen Attributen zu oder steckt sie in Listen oder andere Datenstrukturen. Warum sollte man das nicht machen? Das ist ja immer das *selbe* `Player`-Objekt was dann eben über mehrere Wege erreichbar ist. Überall dort wo man den Zugriff braucht.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Immer wieder interessant, ältere Beiträge von sich zu lesen. Wurde zum Glück nicht peinlich für mich. Ich würde heute sehr ähnlich darauf antworten. :)
schnickalot
User
Beiträge: 22
Registriert: Dienstag 13. August 2019, 14:38

Vielen Dank Blackjack.. das genau war mein Denkfehler. Ich dachte irgendwie immer das sich Instanzen kopieren wenn ich sie von hier nach dort übergebe.. aber klar.. es sind nur neue Referenzen darauf.

Die Konventionen halte ich ein.. frag mich nicht wieso ich's hier anders getippt habe. Außer die Unterstriche.. denke gelesen zu haben man kann es machen ist aber kein muss.

Die Playerlist war ursprünglich mit einer self.list bestückt bevor ich dict vererbt habe.. deswegen der legacy name. Das man list/dict etc nicht im Namen benutzt wusste ich nicht.. guter Hinweis. Wäre sowas wie PlayerPool, PlayerContainer passender oder evtl. einfach das Plural von den Objekten.. Players? Auf dict habe ich umgestellt um die Player per Key direkt anzusprechen anstatt mit for drüber zu iterieren. Und ja.. der fullname ist ein Gebastel aus firstname Leerzeichen und lastname. Das mit Property zu lösen klingt auch gut.

Danke nochmal.. hoffe ich frag nichts zu peinliches.
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@schnickalot: Die Unterstriche sind IMHO ein muss. Sonst wird es schwerer den Code zu lesen. Der Unterstrich fungiert ja als Trennzeichen zwischen Worten wie das Leerzeichen in normalen Texten. Wenn man das weg lässt, dann muss der Leser das im Kopf selbst finden und setzen. Und das ist nicht bei allen Kombinationen so einfach, und selbst bei solchen wo es eindeutig ist, kann es passieren das man erst über die Stelle hinweg liest, bevor ein Namenspräfix als einzelnes Wort keinen Sinn mehr macht, und man dann erst wieder ”suchen” muss wo das nächste Wort in dem man schon mitten drin ist, eigentlich anfängt. Blödes Beispiel: `identry` – da kann man bis `ident` lesen, bevor man merkt, das nach `id` eigentlich ein ”Leerzeichen” gehört und eigentlich `id_entry` gemeint ist. Man kann das sogar komplett als ein Wort aussprechen und müsste wenn man sich nicht sicher ist, Nachschlagen ob es nicht tatsächlich eine Bedeutung für das Wort „identry“ gibt und das vielleicht tatsächlich gemeint ist.

Ein Experiment dafür wäre vielleicht mal einen englischsprachigen Text her zu nehmen und da immer zwei bis drei Worte zu ”einem” zusammen zu fassen und dann mal zu schauen wie (un)flüssig sich das dann liest.

Du hast da ja genau das Problem mit Grunddatentypen in Namen getroffen: Im Laufe der Programmentwicklung ändert man solche Datentypen öfter mal, und muss dann überall die betroffenen Namen ändern, oder hat irreführende weil falsche Namen im Quelltext stehen. Es kommt relativ häufig vor, dass man mit einer Liste anfängt und vielleicht irgendwann feststellt, dass man eigentlich ein `set()` möchte weil man keine doppelten Einträge hat, die Reihenfolge egal ist, aber eine schnelle ``in``-Operation gebraucht wird. Oder das man zwar die Reihenfolge braucht, und über die Elemente iteriert, das aber alles war was man von `list` benötigt, dafür hätte man aber gerne andere Methoden die Zugriff auf die Elemente benötigen. Dann kann man die Liste durch einen eigenen Datentyp ersetzen.

Bei der Benennung einfach die Mehrzahl sowohl bei Klassen als auch bei Objekten die als Sammlung von anderen Objekten verwendet werden. Wobei `Players` schon ein bisschen mehr bieten sollte als einfach nur eine Abbildung von Spielernamen auf Spielerobjekte, denn sonst hätte man auch gleich dort wo man ein `Players`-Objekt erstellt, einfach ein `dict` verwenden können.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
schnickalot
User
Beiträge: 22
Registriert: Dienstag 13. August 2019, 14:38

Ja die Players und Teams Klassen haben noch die pickle Methoden und n paar spezielle Methoden das dict abzufragen.

Nochmals vielen Dank für die Ausführungen. Das macht vieles klarer.

Werde auch mal auf Unterstriche umstellen. Klingt schon vernünftig. Vielleicht noch ein Wort zu kein 1 oder 2 Unterstriche vor dem Attributname.. zZ habe ich 1 Unterstrich davor. Getter und Setter sind zu einem Property (Attributname) zusammengefasst. Passt das so? Ich weiß das 1 Unterstrich eine Konvention darstellt und 2 den internen Namen verbiegen. Aber was nimmt man warum und wann?
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@schnickalot: Was machen denn die `pickle`-Methoden? So aus dem Bauch heraus würde ich ja sagen das gehört nur auf das Objekt wenn die Methode irgend etwas für diesen Typ spezifisches macht, ansonsten ist das eher etwas was man von aussen *mit* dem Objekt macht.

Was heisst „Getter und Setter sind zu einem Property […] zusammengefasst“? Was machen die denn spezielles mit dem Wert? Denn in Python schreibt man keine trivialen Getter und Setter weil die keinen Sinn machen, eben weil es `property()` gibt wenn man aus einem einfachen Attribut etwas machen will wo noch zusätzlich etwas passiert wenn man es abfragt und/oder setzt. Und ein `property()` das einfach nur einen Wert abfragt oder setzt und sonst nix macht, macht keinen Sinn. Den Effekt kann man auch mit weniger Code haben.

Ein `Player` einfach erst einmal nur so als Datenklasse könnte man beispielsweise so schreiben:

Code: Alles auswählen

from attr import attrib, attrs


@attrs
class Player:
    first_name = attrib()
    last_name = attrib()
    team_name = attrib(default=None)
    score = attrib(default=0)
    
    @property
    def full_name(self):
        return f'{self.first_name} {self.last_name}'
Wobei ich hier `team_name` wahrscheinlich durch `team` ersetzen würde, statt den Umweg über einen Namen zu gehen.

Beim `Team` könnte es dann interessant werden das Attribut für die Spieler nicht öffentlich zu machen, damit man die Invariante sichern kann, das Spieler beim hinzufügen auch das passende `team` (oder auch `team_name`) Attribut gesetzt bekommen und vielleicht auch das man nur Spieler zu einem Team hinzufügen kann, wenn das Attribut noch den Wert `None` hat.

Und mindestens den Spieler-Objekten würde ich noch eine ID verpassen, denn das Leute mit gleichen Vor- und Nachnamen in der Realität vorkommen, lässt sich nicht vermeiden. Die muss man dann trotzdem irgendwie auseinander halten können.

Ich persönlich bin mit einem führenden Unterstrich sparsam und setze den meisten nur wenn es wirklich verlockend erscheint direkt etwas mit einem Attribut zu machen *und* dabei leicht etwas kaputt gehen kann. Ansonsten stört mich das in der Regel nicht das Attribute öffentlich sind. Doppelte führende Unterstriche habe ich glaube ich bei normalen Klassen noch *nie* gebraucht. Die sind zur Vermeidung von Namenskollisionen in tiefen Vererbungshierarchien oder bei Mehrfachvererbung. Beides kommt in Python nur sehr selten vor und ist meistens ein Zeichen das man zu viel Komplexität per Vererbung gebastelt hat, die man vielleicht besser anders auflösen sollte.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
schnickalot
User
Beiträge: 22
Registriert: Dienstag 13. August 2019, 14:38

Guten Morgen, auch ein Nachtmensch ;-)

Pickle benutze ich um die Objekte im dict zu sichern. Hab mal gelesen, dass Entwickler darauf achten dass Ihre Klassen gepickled werden können. Dachte somit, dass dies ein gängiger Weg ist, Daten zu sichern. Wenn Statistiken hinzukommen, hab ich mir aber auch schon gedacht auf eine Datenbank zu setzen. Auch hier hab ich mal kurz gehört, dass Python SQLlite mitbringt.

Dein Player-Beispiel wirkt auf mich etwas fortgeschrittener.. soweit bin ich leider noch nicht. Ich hab es ziemlich basic gehalten:

class Player:
def __init__(self, fn, ln, team):
self._first_name = fn
self._last_name = ln
self._full_name = fn + " " + ln
self._team_name = team (hab es team_name genannt, weil ich hier nur den Name setze, anstatt das ganze Team-Objekt)
self._score = 0

def getFirstName(self):
return self._first_name

def setFirstName(self, fn):
self._first_name = fn

first_name = property(getFirstName, setFirstName)

Beim Score war ich mir etwas unsicher wie man die set-Methode schreibt, da ich ja immer nur +1 rechne, ich somit erstmal nix übergeben habe. Da hat aber pyCharm gemeckert (er hat setScore im property-Tuple markiert). Erst als ich einen Übergabe-Wert in die set-Methode gesetzt habe, hat das Gemecker aufgehört:

def getScore(self):
return self._score

def setScore(self, score):
self._score +=score

(ursprünglich)
def setScore(self):
self._score += 1
(/ursprünglich)

score = property(getScore, setScore)

Dachte es ist ein "pythonic" way, propertys zu setzen wo es nur geht. Damit man die Datenkapselung beachtet aber dennoch komfortabel auf die Attribute zugreifen kann.

Ja, eine ID hatte ich auch schon überlegt.. aber mich dann nur für den Fullname als Key entschieden. Nach Monaten weiss keiner mehr welche ID er hatte. Es gibt eine Prüfung ob es den Namen schon gibt, somit muss man dann den Namen anders setzen.. ob mit 2t-Name oder ein -a hinten dran, war mir erstmal Wurscht. Wird wahrscheinlich eh nicht nötig sein. Soll ja nix Kommerzielles werden.. eigentlich nur für mich zum üben und evtl für unsere Heimspiele. Wenn andere Mannschaften scharf drauf werden sollten, werde ich es eh nochmal überdenken und professioneller angehen. Finde ja allein die Hardware (Settop-Box) ist nicht optimal um kommerziell Software anzubieten. Dann wird es wohl eher ein RasbPi mit Touchscreen und Handy-App zum steuern. Bisher aber alles Träumerei!

Alles was ich bisher hier besprochen habe, habe ich Codetechnisch bisher nur getippt. Das ganze Program läuft ja eigentlich schon, aber bisher nur mit Player und Teamobjekten und deren Listenobjekten (ohne dict-Vererbung) inkl. pickle. Game und Match Objekte waren bisher nicht Bestandteil. Auch propertys hatte ich bisher keine.. auch keine list comprehensives für etwaige Listenabfragen. Das schreibe ich seit Tagen alles um.. und da kamen dann all diese Fragen auf. Wollte es eigentlich nicht noch öfter umschreiben, deswegen versuche ich hier mein <Halbwissen aufzubessern. Aber dein Beispiel hat mich auch auf den Boden zurückgebracht.. das ist ja völlig anders wie ich es je in Betracht gezogen habe. Aber decorators sind für mich auch insgesamt noch ein eigenes Kapitel.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Der Zugriff von außen ist für Attribute und Properties gleich, es macht also keinen Sinn, dort, wo auch ein Attribut-Zugriff reichen würde eine Property zu schreiben. Und für score kannst Du entweder player.score += 1 schreiben, oder eine player.increase_score()-Methode.

Wenn es ein Team-Objekt gibt, dann setze das auch, und nicht nur den Namen. An den Namen kommt man ja über das Team ran.

Benutze keine Abkürzungen, vor allem nicht bei Funktions-Argumenten, denn die sind ja über `help` die halbe Dokumentation.

Code: Alles auswählen

class Player:
    def __init__(self, first_name, last_name, team):
        self.first_name = first_name
        self.last_name = last_name
        self.team = team
        self.score = 0

    @property
    def full_name(self):
        return f"{self.first_name} {self.last_name}"

    def increase_score(self):
        self.score += 1
Fürs pickeln muß man normalerweise nichts selber machen. Aber wie schon geschrieben, ist das auch nicht für längfristige Speicherung gedacht.
schnickalot
User
Beiträge: 22
Registriert: Dienstag 13. August 2019, 14:38

Danke Dir. D.h. also ich kann/soll sogar alles über direkte Attributzugriffe machen? Bisher habe ich immer gelesen, dass man bei OOP die Datenkapselung beachten sollte.. und Python dafür extra das property Konstrukt bietet, damit man codetechnisch Methoden umgehen kann aber trotzdem benutzt. Bin ein wenig verwirrt.

Das mit dem Teamobjekt statt nur dem Namen werde ich beherzigen. Bin ja seit gestern schlauer wie man mit Objekten umgeht/umgehen kann.
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Datenkapselung von etwas auf das man dann gleich wieder vollen Zugriff als properties gibt ist ja nun ziemlich wertfrei. Der einzige Grund warum einige andere sprachen (allen voran Java) getter & Setter haben liegt in deren Unzulänglichkeit. Es gibt dort keine Möglichkeit mit Attributs-Zugriff-Syntax trotzdem Code laufen zu lassen. Wir zb für berechnete werte. Darum packt man einfach ALLES in getter/setter, und wenn die mal was anderes als nur Durchreichen, dann schön.

Python hingegen kennt eben properties, so das du mit Attribut Zugriff Code laufen lassen kannst. Aber natürlich nur, wenn dabei auch was anderes passiert. Nicht für einfaches lesen & setzen.
schnickalot
User
Beiträge: 22
Registriert: Dienstag 13. August 2019, 14:38

ah ok.. also spricht man Attribute direkt an, wenn man sie nur ausliest und setzt.. bei mehr code nimmt man propertys?!

Gibt es eigentlich eine gute Literatur, wo man "best practices" erlernen kann? Die Grundlagenbücher geben ja nur einen kleinen Einblick in "so sollte man es machen".
Antworten