Gewinnspiel

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.
Gary123456
User
Beiträge: 318
Registriert: Dienstag 26. Februar 2013, 18:39

Vor lauter Begeisterung habe ich das mal auf mein Programm angewendet und bin mir nicht zu 100% sicher, ob das stimmt (es funktioniert aber!)

Code: Alles auswählen


import easygui, random, sys


class Spiel:

    def __init__(self, spielername):
        self.name = spielername
        self.liste = [
	
	("Was ist Fifa13?", ["Ein Computerspiel", "Ein Monster", "Eine Uhrzeit"]),
 	("Was ist eine Computermaus?", ["Ein Steuerungsgeraet", "Eine Tastatur", "Ein Bildschirm"]), 
	("Test1", ["Test2", "Test3", "Test4"])
        ]
        self.punkte = 0
        self.versuche = 3
        self.geld = 0

    def __str__(self):
        hinweis = "Das ist ein klassisches 'Wer wird Millionär' Spiel"
        return hinweis

    def punkte_hinzufuegen(self, betrag): #Normal sind 100 Punkte
        self.punkte += betrag
        return self.punkte

    def punkte_abziehen(self, betrag):
        if self.punkte > 100:
            self.punkte -= betrag
            return self.punkte
        else:
            print "Sie haben das Spiel verloren!"

    def geld_hinzufuegen(self, betrag):
        self.geld += betrag
        return self.geld

    def geld_abziehen(self, betrag): #pro Frage wahrscheinlich 100 € weg
        if self.geld >= 100:
            self.geld -= betrag
            return self.geld
        else:
            print "Sie haben das Spiel verloren!"
    def punkte_anzeigen(self):
        return self.punkte

    def geld_anzeigen(self):
        return self.geld
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Gary123456 hat geschrieben:

Code: Alles auswählen

    def punkte_anzeigen(self):
        return self.punkte
punkte_anzeigen ist der falsche Name für die Methode. Da wird nichts angezeigt, sondern nur ein Wert zurückgegeben.

Zudem ist die Methode überflüssig. Wenn du die Punkte haben willst, dann greif doch einfach direkt auf name_der_spiel_instanz.punkte zu, statt den unnötigen Umweg zu gehen.

An einer anderen Stelle ist es ebenfalls merkwürdig. Die Methode punkte_abziehen tut nicht nur das was ihr Name sagt, sondern gibt auch noch die neue Punktzahl als Wert zurück. Schlimmer noch, in der Methode mischst du wieder Ein-/Ausgabe mit Logik. Lass das Spiel ein Spiel sein und erledige Ausgaben außerhalb.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Was sollen die Kommentare "Normal sind …" und "pro Frage wahrscheinlich …"? Es ist zwar bekannt, dass Software in der Regel nicht-deterministisch ist, aber das so deutlich in den Kommentaren auszudrücken, ist doch ungewöhnlich ;) Also tu zumindest so, als ob sich dein Programm ganz verlässlich immer so verhält wie du es erwartest und schreibe konkretere Kommentare. Anstelle der magischen 100 bietet sich eine Klassenkonstante an, um den Code sowohl lesbarer als auch wartbarer zu machen.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Gary123456: Deinen Code habe ich mal etwas umgestellt. Das ist jetzt nur ganz rudimentär ohne __name__ == '__main__' etc. und soll Dir beispielhaft zeigen, wie Du Logik und IO trennen kannst, und dass für den Attributzugriff keineswegs getter und setter gebraucht werden. Und 'property' habe ich Dir deswegen auch eingebaut.

Code: Alles auswählen

FRAGEN = [
        ("Was ist Fifa13?", 
            ["Ein Computerspiel", "Ein Monster", "Eine Uhrzeit"]),
        ("Was ist eine Computermaus?", 
            ["Ein Steuerungsgeraet", "Eine Tastatur", "Ein Bildschirm"]), 
        ("Test1", 
            ["Test2", "Test3", "Test4"])
        ]

GRENZWERT = 100


class Spiel(object):

    def __init__(self, spielername, fragen, grenzwert):
        self.name = spielername
        self.fragen = fragen
        self.grenzwert = grenzwert
        self.punkte = 0

    def __str__(self):
        return u"Das ist ein klassisches 'Wer wird Millionär' Spiel"

    @property
    def ist_zuende(self):
        return self.punkte < self.grenzwert

        
# Beispiel:

# Erzeuge Spiel-Objekt mit Frageliste
spiel = Spiel('Gary', FRAGEN, GRENZWERT)

# direkter Zugriff auf Instance-Variable
spiel.punkte += 500
print spiel.punkte

# Ablaufkontrolle
if spiel.ist_zuende:
    print 'Spiel verloren'
else:
    print 'Spiel geht weiter'
    
# Stelle Fragen
for frage in spiel.fragen:
    print frage
    ...
Das musst Du jetzt nur noch passend konstruieren. Elegant wäre es, wenn auch die Fragen Objekte mit Attributen wären, so dass Du etwas wie das folgende schreiben könntest:

Code: Alles auswählen

for frage in spiel.fragen:
    print frage.fragetext
    ...
    if antwort == frage.antwort:
        spiel.punkte += 100
    else:
        spiel.punkte -= 100
    if spiel.ist_zuende:
        break
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@kbr: und hier machst Du zu viel Spiel-relevantes außerhalb der Spiel-Klasse.
Das Spiel soll selbst entscheiden, ob es weitere Fragen gibt, oder ob es zu Ende ist.
Das Spiel soll selbst entscheiden, was passiert, wenn eine Frage richtig oder falsch beantwortet wird:

Code: Alles auswählen

FRAGEN = [
        ("Was ist Fifa13?",
            ["Ein Computerspiel", "Ein Monster", "Eine Uhrzeit"]),
        ("Was ist eine Computermaus?",
            ["Ein Steuerungsgeraet", "Eine Tastatur", "Ein Bildschirm"]),
        ("Test1",
            ["Test2", "Test3", "Test4"])
        ]

GRENZWERT = 100


class Spiel(object):
    def __init__(self, spielername, fragen, grenzwert):
        self.name = spielername
        self._fragen = fragen
        self.grenzwert = grenzwert
        self.punkte = grenzwert

    def __str__(self):
        return u"Das ist ein klassisches 'Wer wird Millionär' Spiel"

    @property
    def fragen(self):
        for frage in fragen:
            if self.punkte < self.grenzwert:
                break
            yield frage

    def verarbeite_antwort(self, antwort):
        self.punkte += 100 if antwort else -100
       
spiel = Spiel('Gary', FRAGEN, GRENZWERT)

# Stelle Fragen
for fragetext, antworten in spiel.fragen:
    print fragetext
    ...
    spiel.verarbeite_antwort(antwort == richtige_antwort)
    print "Du hast jetzt %d Punkte" % spiel.punkte
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Sirius3: Du hast ja recht und mit einer Antwort in diese Richtung hatte ich auch gerechnet, denn das Spiel-Objekt in meinem Beispiel hält im wesentlichen nur den Status fest - ganz rudimentär, so wie ich schrieb.
Aber lass Gary an einfachen Beispielen erst einmal die Nutzung von Klassen üben, eventuell auch für Fragen eine Klasse erstellen und dann, folgerichtig, auch die Spiellogik in die Klasse aufnehmen und dabei lernen, was Refactoring ist, und das dies ein beständiger Prozess bei der Softwareentwicklung darstellt.
Deine Erweiterung mag ihm später auch helfen, zuvor aber wirst Du ihm vermutlich erst einmal 'yield' erklären müssen. Und warum Du aus 'self.fragen' 'self._fragen' machen musstest, was Du zudem nicht verwendest.
Gary123456
User
Beiträge: 318
Registriert: Dienstag 26. Februar 2013, 18:39

Hi,

@me)
Ja, da hast Du sicher Recht! :)

@derdon)
Eig ist es doch mein Bier, was ich in den Kommentaren schreibe. :)

@Sirius + kbr)

Mein Beispiel war nur ein kleiner Einstige, wie man Klassen genau verwendet. Es wird sicher nicht ins reale Programm einfließen, bis ich noch nicht die nötige Übung habe. Bis dahin werde ich zwar am Millionär-Spiel das üben, aber nicht einsetzen, bis der Code richtig ist. Und ich habe auch verstanden, was Ihr sagen wollt :) Habe auch als Übung eine Bankklasse entwickelt und sie funktioniert, jedoch habe ich den Code nicht mehr.
Aber lass Gary an einfachen Beispielen erst einmal die Nutzung von Klassen üben, eventuell auch für Fragen eine Klasse erstellen und dann, folgerichtig, auch die Spiellogik in die Klasse aufnehmen und dabei lernen,
Richtig. Ich will an diesem "Millionär-Beispiel" üben. Obwohl - eig ist es ja das Hauptspiel.

Doch dann noch eine Frage an Dich, lieber Sirius:

Code: Alles auswählen

class Spiel(object):
Was erbst Du da?

Aber jetzt muss ich zu SQL rüber, aber keine Sorge, schulisch bedingt. Mir schreiben in gute 3 Wochen eine Arbeit und da will ich unbedingt gut sein :) Daher - werde ich evtl. nicht mehr so viel Zeit haben. Wie lässt sich SQL eig in Python einsetzen?
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@kbr: Dass self._fragen nicht benutzt wird, ist der obligatorische Programmierfehler, weswegen man das gesamte Programm für unbrauchbar erklärt und genervt wegschmeißt.

@Gary123456: unter Python 2.x gibt es zwei verschiedene Arten von Klassen, klassische und neue. Neue haben erweiterte Funktionen und werden dadurch definiert, dass sie von »object« abstammen.

Python hat zu allen wichtigen Datenbanken Module zur Anbindung. Einfach mal google z.B. nach »python sqlite« fragen.
Gary123456
User
Beiträge: 318
Registriert: Dienstag 26. Februar 2013, 18:39

Neue haben erweiterte Funktionen und werden dadurch definiert, dass sie von »object« abstammen.
Damit mkeinst Du noch so weitere Funktionen wie die init und str Methode? :)

Ich versteh immer mehr, dennoch versuche ich alles langsam anzugehen :)
Gary123456
User
Beiträge: 318
Registriert: Dienstag 26. Februar 2013, 18:39

Ich will jetzt auf Nummer sicher gehen. Ich werde euch meinen Gedankenschritt sagen, wie ich bei einer Erstellung einer Klasse vorgehen würde.

- Zuerst überlege ich mir, welche Eigenschaften das Spiel an sich hat (z.B. Geld, Punkte etc. => Attribute). Die lege ich in die __init__ Funktion
- danach überlege ich mir, was das Spiel können muss und erstelle Methoden.

Eine kurzer Gedankenschritt, sollte aber richtig sein :)
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Gary123456 hat geschrieben:
Neue haben erweiterte Funktionen und werden dadurch definiert, dass sie von »object« abstammen.
Damit mkeinst Du noch so weitere Funktionen wie die init und str Methode? :)
Nein, Funktionen im Sinne von "Funktionalitaet". Beispielsweise unterstuetzen sie das Deskriptor-Protokoll, Properties, Metaklassen, ...
Gary123456
User
Beiträge: 318
Registriert: Dienstag 26. Februar 2013, 18:39

Nein, Funktionen im Sinne von "Funktionalitaet". Beispielsweise unterstuetzen sie das Deskriptor-Protokoll, Properties, Metaklassen, ...
Ich denke, dass ist für meinen derzeitigen Wissenstand unwichtig ;)
Gary123456
User
Beiträge: 318
Registriert: Dienstag 26. Februar 2013, 18:39

Bild

Das sind meine Attribute, die ich in der __init__ Funktion angeben werde. Das ist mal vorerst der Plan.

Von den Methoden hätte ich mir 4 überlegt:

- Punkte abziehen
- Punkte erhalten
- Geld abziehen
- Geld erhalten

Diese Methoden könnte man zwar mit self.punkte += betrag abkürzen, dennoch finde ich Methoden übersichtlicher.

Die reine Klasse hier:

Code: Alles auswählen


import easygui, random, sys


class Spiel:

    def __init__(self, spielername, punkte, geld, schwierigkeit, level, thema, fragen):
        self.spielername = spielername
        self.punkte = punkte
        self.geld = geld
        self.schwierigkeit = schwierigkeit
        self.level = level
        self.thema = thema
        self.fragen = fragen

    def __str__(self):
        nachricht = "Das ist ein Spiel" #Wird zur Hilfe ausgebaut!
        return nachricht

    def punkte_abziehen(self, betrag):
        self.punkte -= betrag

    def punkte_aufnehmen(self, betrag):
        self.punkte += betrag

    def geld_abziehen(self, betrag):
        self.geld -= betrag

    def geld_aufnehmen(self, betrag):
        self.geld += betrag

Ich denke, jetzt ist die Logik besser.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Gary123456 hat geschrieben:Von den Methoden hätte ich mir 4 überlegt:

- Punkte abziehen
- Punkte erhalten
- Geld abziehen
- Geld erhalten
Kurzum: Die sind alle schlecht.

Bei Methoden musst du dich fragen, ob sie im Kontext der Klasse als Schnittstelle nach aussen Sinn machen. Alle 4 Methoden erfuellen das nicht, weil der Aufrufende hier folgende Fragen beantworten muss: "Ist die Frage richtig beantwortet?", "Wie viele Punkte ist diese Antwort wert?" usw.

Je nachdem wie man die Klasse benutzt kann das aber durchaus auch kein Problem sein, aber die Methoden haben ein weiteres Problem: Sie sind trivial, aber machen das Benutzen und Verstehen nicht einfacher.

Klassendesign ist leider etwas fuer das man Erfahrung braucht und das man nicht von jetzt auf gleich erlernen kann. Ein guter Ansatz ist sich in den anwendenden Code hineinzuversetzen und die Frage zu beantworten "Was muss die Klasse dafuer leisten koennen". Entwickle die Klasse nicht so, dass sie am Anfang direkt alle Anforderungen erfuellt (denn hier kennst du wahrscheinlich noch nicht alle), sondern iterativ, so dass sie alles leistet was sie leisten muss.

@__str__: Die Funktion soll eine "menschenlesbare Repraesentation" liefern, die den Status repraesentiert, also hier beispielsweise die Werte der Attribute.

Zu den Newstyle-Klassen: Dann benutze es nicht, aber tu dir den Gefallen und nutze sie einfach.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Klassen sollten vor allem Funktionalität liefern. Sie sollen nicht ihre (internen) Attribute widergeben, sondern sie sollen damit *arbeiten*. Klassen erfüllen in erster Linie Aufgaben. Natürlich sind sie manchmal auch ein Datenspeicher und natürlich sind auch gewisse Getter-Methoden in Ordnung. Es geht aber vor allem darum, dass die Klasse tatsächlich etwas tut und nicht nur verwaltet.
Gary123456
User
Beiträge: 318
Registriert: Dienstag 26. Februar 2013, 18:39

Hi,
Bei Methoden musst du dich fragen, ob sie im Kontext der Klasse als Schnittstelle nach aussen Sinn machen. Alle 4 Methoden erfuellen das nicht, weil der Aufrufende hier folgende Fragen beantworten muss: "Ist die Frage richtig beantwortet?", "Wie viele Punkte ist diese Antwort wert?" usw.
Meinst Du damit, dass man einen bestimmten Wert einsetzen muss (den Wert, der z.B. die Punkteanzahl bestimmt? Denn dann würde Frage2 wegfallen. Ich versteh nicht genau, was Du meinst.
Klassendesign ist leider etwas fuer das man Erfahrung braucht und das man nicht von jetzt auf gleich erlernen kann. Ein guter Ansatz ist sich in den anwendenden Code hineinzuversetzen und die Frage zu beantworten "Was muss die Klasse dafuer leisten koennen".
Klassen sind wie Baupläne. Daher will ich mit Hilfe dieser Klasse einen Bauplan für mein Projekt erstellen.
Entwickle die Klasse nicht so, dass sie am Anfang direkt alle Anforderungen erfuellt (denn hier kennst du wahrscheinlich noch nicht alle), sondern iterativ, so dass sie alles leistet was sie leisten muss.
Das versuche ich auch.
Zu den Newstyle-Klassen: Dann benutze es nicht, aber tu dir den Gefallen und nutze sie einfach.
Wenn Du meinst, benutze ich sie auch.
Klassen sollten vor allem Funktionalität liefern. Sie sollen nicht ihre (internen) Attribute widergeben, sondern sie sollen damit *arbeiten*. Klassen erfüllen in erster Linie Aufgaben. Natürlich sind sie manchmal auch ein Datenspeicher und natürlich sind auch gewisse Getter-Methoden in Ordnung. Es geht aber vor allem darum, dass die Klasse tatsächlich etwas tut und nicht nur verwaltet.
Jep. Aber was ist denn bitteschön eine Getter-Methode?

Und:
In SQL kann man das in ER-Diagramme unterbringen. Wie kann ich das am Besten in Python umwandeln. Also ein Python Diagramm , dass Atribute und Methoden veranschaulicht. Das würde mir sicher sehr viel erleichtern.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Gary123456 hat geschrieben:Jep. Aber was ist denn bitteschön eine Getter-Methode?
Vergiss das erstmal. Aber das Stichwort 'property' ist ja schon gefallen. Behalte das einfach mal im Hinterkopf.
Gary123456 hat geschrieben:In SQL kann man das in ER-Diagramme unterbringen. Wie kann ich das am Besten in Python umwandeln. Also ein Python Diagramm , dass Atribute und Methoden veranschaulicht. Das würde mir sicher sehr viel erleichtern.
Das Stichwort hier heißt UML-Diagramm.
Gary123456
User
Beiträge: 318
Registriert: Dienstag 26. Februar 2013, 18:39

Vielen, vielen Dank!

Man könnte doch insgesamt zwei Klassen erstellen, eine für Spiel, und eine Klasse Spieler, die von Spiel erbt. Nun frägt man sich, ob es ohne Klassen besser gehen würde. Ich muss mir da mal ganz klare Gedanken machen.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Gary123456 hat geschrieben:Man könnte doch insgesamt zwei Klassen erstellen, eine für Spiel, und eine Klasse Spieler, die von Spiel erbt.
Vererbung ist eine "ist-ein"-Beziehung, aber ein Spieler ist kein Spiel, also macht die Vererbung da keinen Sinn.
Gary123456 hat geschrieben:Nun frägt man sich, ob es ohne Klassen besser gehen würde. Ich muss mir da mal ganz klare Gedanken machen.
Du kannst da durchaus eine Klasse nutzen, wenn du magst.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Gary123456
User
Beiträge: 318
Registriert: Dienstag 26. Februar 2013, 18:39

Du kannst da durchaus eine Klasse nutzen, wenn du magst.
Naja, die Frage ist ja was sinnvoller ist. Eine Klasse voller Attribute? Eine Klasse, die auch ohne Methoden auskommt, da mir keine sinnvollen einfallen?
Vererbung ist eine "ist-ein"-Beziehung, aber ein Spieler ist kein Spiel, also macht die Vererbung da keinen Sinn.
Jep. Du kannst aber zwei Klassen erstellen. Eine Spiel Klasse, die die Fragen verwaltet. Und eine Spieler Klasse, die Punkte, Geld etc. verwaltet und paar Methoden hat.
Antworten