Klasse, Liste, änderung..

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.
Antworten
Brpo25
User
Beiträge: 7
Registriert: Donnerstag 6. Januar 2022, 11:17

Hallo,
ich überschreibe gerade einen Getränkeautomat als Hausaufgabe. Lehrer sagte ist alles in Ordnung, nur eine letzte Methode fehlt, die nach dem Getränkausgabe die Anzahl restlichen Flaschen ändert. Mithilfe eine Klasse

Code: Alles auswählen

class Getraenk:
    
    def __init__(self, name, preis, anzahl):
        self.name_getraenk = name
        self.preis_pro_flasche = preis
        self.anzahl_flaschen = anzahl

    def get_name_getraenk(self):
        return self.name_getraenk
         
    def get_anzahl_flaschen(self):
        return self.anzahl_flaschen
    
    def get_preis_pro_flasche(self):
        return self.preis_pro_flasche
habe ich mehrere Instanzen von Getränk erzeugt und in die Liste getraenke_liste gespeichert:

Code: Alles auswählen

class Getraenkeautomat:
    
    def __init__(self, temp_zahlomat):
        self.zahlomat = temp_zahlomat
        self.getraenke_liste = []

        getraenk = self.getraenke_liste.append(Getraenk("Wasser", 5, 2))
        getraenk = self.getraenke_liste.append(Getraenk("Limonade", 6, 20))
        getraenk = self.getraenke_liste.append(Getraenk("Bier", 7, 10))
        getraenk = self.getraenke_liste.append(Getraenk("Red-Bull", 8, 0))
Jetzt habe ich Problem auf die liste zugreifen und ein Anzahl von gewünschten Getränk ändern. Ich sollte die Änderung über eine Methode ausführen. Ich bekomme den Wert von Anzahl über die Methode get_anzahl_flashen () kann den Wert aber nicht ändern. Wollte das alleine klären, aber dazu habe ich noch nicht die Fähigkeiten.
Danke euch
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der Code im __init__ von Getraenkeautomat ist falsch. Die append-Methode liefert None zurueck, an getraenk wird also kein (sinnvoller) Wert gebunden. Und das 4 mal hintereinander zu machen zeigt ja auch, dass du das im Grunde gar nicht brauchst.

Dann schreibt man in Python keine getter und setter. Stattdessen liest man einfach die Attribute der Klasse direkt aus. Dein Getraenk schrumpft also zusammen zu einer simplen Datenklasse, wie man sie mit dem dataclass-Modul trivial erstellt.

Und wenn man ein solches Attribut aendern will, dann macht man das zB mit einem simplen

Code: Alles auswählen

getraenk.anzahl -= 1
Ein vereinfachtes Beispiel (ohne jede Fehlerpruefung, und ob genug Geld eingezahlt wurde, etc.)

Code: Alles auswählen

from dataclasses import dataclass


@dataclass
class Getraenk:
    name: str
    preis: float
    anzahl: int


class GetraenkeAutomat:

    def __init__(self):
        self._getraenke = [
            Getraenk("Cola", 1.5, 10),
            Getraenk("Wasser", 1.0, 10),
        ]

    def kaufe(self, index):
        getraenk = self._getraenke[index]
        getraenk.anzahl -= 1


    def __repr__(self):
        return f"<{self.__class__.__name__}: {self._getraenke}"

def main():
    automat = GetraenkeAutomat()
    automat.kaufe(0)
    print(automat)


if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 18279
Registriert: Sonntag 21. Oktober 2012, 17:20

Als erstes sind diese ganzen Getter überflüssig und können weg. Bleibt also

Code: Alles auswählen

class Getraenk:
    
    def __init__(self, name, preis, anzahl):
        self.name_getraenk = name
        self.preis_pro_flasche = preis
        self.anzahl_flaschen = anzahl
temp_zahlomat, was hat das mit Temperatur zu tun? Benutze keine kryptischen Abkürzungen, das verwirrt nur.
Datentypen haben nichts in Namen verloren, `getraenke` ist viel besser `getraenkeliste`
`append` hat keinen Rückgabewert, zumal du mit `getraenk` auch gar nichts machst. Statt 4 mal append aufzurufen, kannst Du die Liste auch gleich direkt erzeugen:

Code: Alles auswählen

class Getraenkeautomat:
    def __init__(self, zahlomat):
        self.zahlomat = zahlomat
        self.getraenke = [
            Getraenk("Wasser", 5, 2),
            Getraenk("Limonade", 6, 20),
            Getraenk("Bier", 7, 10),
            Getraenk("Red-Bull", 8, 0),
        ]
Du redest bei Deiner Frage von einer Methode, die sehe ich aber gar nirgends.
Attribute ändert man wie normale Variablen auch:

Code: Alles auswählen

getraenk.anzahl_flaschen -= 17
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Brpo25: Die `Getraenk`-Klasse ist zu lang. Man schreibt in Python keine trivialen Getter/Setter-Methoden sondern greift direkt auf die Attribute zu.

Bei `name_getraenk` ist das `_getraenk` überflüsseg, denn durch den Kontext der Klasse ist das ja schon klar, das es sich um den Namen vom Getränk handelt.

Wenn es keinen guten Grund gibt die Argumentnamen der `__init__()` anders zu benennen als die Attributnamen unter denen die Werte dann auf dem Objekt abrufbar sind, sollte man das nicht machen. Es ist leichter verständlich wenn diese Namen gleich sind, und der Leser nicht raten muss welches Argument wohl zu welchem Attribut wird.

Und was der Zusatz `temp_` bei dem Argument bei `Getraenkeautomat.__init__` bedeuten soll, wirft auch mehr Fragen auf als es beantwortet. Das sollte einfach nur `zahlomat` heissen.

Grunddatentypen haben in Namen nichts verloren. Den Typ ändert man im Laufe der Programmierung gerne mal, ersetzt den Containertyp durch einen anderen mit passenderen Operationen, oder gar durch einen eigenen Datentyp, und schon muss man entweder durch den Quelltext gehen und alle betroffenen Namen ändern, oder man hat irreführende, weil falsche, Namen im Quelltext stehen.

`getraenk` wird nicht verwendet und hat auch überhaupt gar keinen sinnvollen Wert den man verwenden könnte. `list.append()` gibt `None` zurück. Warum bindest Du das an einen Namen?

Statt da mehrmals `append()` aufzurufen würde man die Getränke aber auch direkt in das Listenliteral schreiben.

Der Quelltext sieht dann so aus:

Code: Alles auswählen

class Getraenk:
    def __init__(self, name, preis_pro_flasche, flaschenanzahl):
        self.name = name
        self.preis_pro_flasche = preis_pro_flasche
        self.flaschenanzahl = flaschenanzahl


class Getraenkeautomat:
    def __init__(self, zahlomat):
        self.zahlomat = zahlomat
        self.getraenke = [
            Getraenk("Wasser", 5, 2),
            Getraenk("Limonade", 6, 20),
            Getraenk("Bier", 7, 10),
            Getraenk("Red-Bull", 8, 0),
        ]
Welches Problem hast Du denn nun konkret? Wie sieht die Methode aus? Was bekommt die als Argument(e) übergeben?
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Brpo25
User
Beiträge: 7
Registriert: Donnerstag 6. Januar 2022, 11:17

Ich lerne es erst ein Paar Tagen. Mache ein Fernstudium bei ILS. Ich sollte ein Code aus meinem Studienheft bearbeiten, dass es funktioniert. Wir sind da gerade beim Klassen und Datenkapselung. Habe schon über diese Aufgabe im Internet gelesen, und viel sagen, dass es nicht gut geschrieben ist. Über __del__ Methoden usw. Das mit dem append. kommt von Lehrer, er hat das geschrieben als Beispiel, so ich habe das benutzt und es sollte mehr Getränke geben. Hier ist die Aufgabe mit dem Code:
1."Erstellen Sie eine Klasse für Getränke. Die Klasse soll den Namen des Getränks, den Preis pro Flasche und die noch vorhandene Anzahl Flaschen speichern können.
Ersetzen Sie dann in dem Getränkeautomaten aus diesem Studienheft die Attribute für die Getränke durch Instanzen der Klasse für die Getränke. Achten Sie dabei bitte auf die Datenkapselung."
2."Unser Getränkeautomat ist ein „Betrüger“. Der Preis für die Flaschen wird berech
net, bevor geprüft wird, ob noch genügend Flaschen vorhanden sind. Es erfolgt auch keine Rückgabe des eventuell zu viel gezahlten Geldes.
Ändern Sie den Getränkeautomaten so, dass vor der Berechnung des Gesamtpreises geprüft wird, ob noch genügend Flaschen des gewünschten Getränks vorhanden sind. Wenn nicht, muss der Gesamtpreis aus der noch maximal verfügbaren Anzahl Flaschen berechnet werden."

Code: Alles auswählen

""" ********************************
Der Getränkeautomat Version 6
mit der Methode __init__()und __del__()
************************************"""
# die Vereinbarung der Klasse für die Münzeinheit
class Muenzeinheit:
    # die Methode __init__()
    def __init__ (self):
        # die Attribute
        self.betrag = 0
        self.noch_zu_zahlen = 0
        self.rueckgeld = 0

    # die Methode __del__()
    def __del__(self):
        print("Eine Instanz der Klasse Muenzeinheit wurde gelöscht.")
        
    # die weiteren Merhoden
    def muenzen_annehmen(self, wert):
        self.noch_zu_zahlen = self.noch_zu_zahlen - wert

    def rueckgeld_geben(self):
        # den absoluten Betrag von noch_zu_zahlen als Rückgeld liefern
        self.rueckgeld = abs(self.noch_zu_zahlen)
        return self.rueckgeld

    def set_betrag(self, preis):
        self.betrag = preis
        self.noch_zu_zahlen = self.betrag

    def get_noch_zu_zahlen(self):
        return self.noch_zu_zahlen

# die Vereinbarung der Klasse für den Automaten
class Getraenkeautomat:
    # übergeben werden die Anzahl und eine Referenz auf die Münzeinheit
    def __init__(self, anzahl1, anzahl2, anzahl3, temp_zahlomat):
        # die Attribute
        # jetzt ist die Münzeinheit Teil des Automaten
        self.zahlomat = temp_zahlomat

        # eine leere Liste für die Gertänkenamen und die Anzahl der Flaschen
        self.getraenk = []
        self.anzahl_flaschen = []

        # die Gertänke eintragen
        self.getraenk.append("Limonade")
        self.getraenk.append("Wasser")
        self.getraenk.append("Bier")

        # die Anzahl der Flaschen
        # Sie werden jetzt durch die Argumente gesetzt
        self.anzahl_flaschen.append(anzahl1)
        self.anzahl_flaschen.append(anzahl2)
        self.anzahl_flaschen.append(anzahl3)

        # die Kühlung ist aus
        self.kuehlung = False

    def __del__(self):
        print("Eine Instanz der Klasse Getraenkeautomat wurde gelöscht.")
        
    def getraenke_waehlen(self):
        # die Auswahl
        print("Bitte wählen sie ein Gertänk:")
        print("Es gibt folgende Auswahl:")
        anzeige_auswahl = 1
        for getraenk in self.getraenk:
            print(anzeige_auswahl, getraenk)
            anzeige_auswahl = anzeige_auswahl + 1

        auswahl = int(input("Geben Sie die gewünschte Nummer ein: "))

        # gibt es noch Flaschen vom gewählten Gertänk?
        if self.anzahl_flaschen[auswahl - 1] != 0:
            # die Anzahl der Flaschen einlesen
            anzahl = int(input("Wie viele Flaschen möchten Sie? "))

            # erst muss bezahlt werden
            # der Preis 10 ist fest vorgegeben
            print("Sie müssen", anzahl * 10, "Cent bezahlen.")
            self.zahlomat.set_betrag(anzahl * 10)
            while self.zahlomat.get_noch_zu_zahlen() > 0:
                print("Es fehlen noch", self.zahlomat.get_noch_zu_zahlen(), "Cent.")
                self.zahlomat.muenzen_annehmen(3)

            # das Getränk ausgeben
            auswahl = auswahl - 1
            self.getraenk_ausgeben(anzahl, auswahl)
        else:
            print("Das gewählte Getränk ist leider nicht mehr vorhanden.")
            auswahl = - 1

        return auswahl

    def getraenk_ausgeben(self, anzahl, getraenke_index):
        # gibt es noch genügend flaschen?
        if anzahl <= self.anzahl_flaschen[getraenke_index]:
            print("Sie erhalten", anzahl, "Flasche(n)", self.getraenk[getraenke_index])
            self.anzahl_flaschen[getraenke_index] = self.anzahl_flaschen[getraenke_index] - anzahl
        else:
            print("Es sind nur noch", self.anzahl_flaschen[getraenke_index], "Flaschen", self.getraenk[getraenke_index], "vorhanden.")
            print("Sie  erhalten den Rest.")
            self.anzahl_flaschen[getraenke_index] = 0
        # Geld zurückgeben
        print("Sie erhalten", self.zahlomat.rueckgeld_geben(),"Cent zurück,")

    def kuehlen(self, an_aus):
        self.kuehlung = an_aus
        if self.kuehlung == True:
            print("Die Kühlung ist eingeschaltet.")
        else:
            print("Die Kühlung ist ausgeschaltet.")

# eine Münzeinheit erzeugen
zahlomat = Muenzeinheit()
# einen Automaten erzeugen
#die Münzeinheit und die Anzahl der Getränke werden übergeben
automat = Getraenkeautomat(10, 20, 30, zahlomat)

auswahl = - 1
# die Kühlung einschalten
automat.kuehlen(True)

# ein Getränk auswählen
while auswahl == -1:
    auswahl = automat.getraenke_waehlen()

# die Kühlung ausschalten
automat.kuehlen(False)

# die Instanzen ausdrücklich freigeben
# zuerst den Automaten
del automat
# und die Münzeinheit
del zahlomat
Also habe ich versucht es zu machen. Erstes mal war Komplett alles falsch und nach ein paar Besprechungen habe ich das so geändert, und Lehrer wollte dazu noch eine Methode, wegen Datenkapselung, die die Anzahl ändert. Mein Code gerade mit Fehlern:

Code: Alles auswählen

""" ********************************
Der Getränkeautomat Version 6
mit der Methode __init__()und __del__()
************************************"""
# die Vereinbarung der Klasse für die Getränke
class Getraenk:
    
    # die Merhode __init__() mit wert übergabe
    def __init__(self, name, preis, anzahl):

        # die Attribute
        self.name_getraenk = name
        self.preis_pro_flasche = preis
        self.anzahl_flaschen = anzahl

    # die weitere Methoden
    def get_name_getraenk(self):
        return self.name_getraenk
         
    def get_anzahl_flaschen(self):
        return self.anzahl_flaschen
    
    def get_preis_pro_flasche(self):
        return self.preis_pro_flasche
    
# die Vereinbarung der Klasse für die Münzeinheit
class Muenzeinheit:
    # die Methode __init__()
    def __init__ (self):
        # die Attribute
        self.betrag = 0
        self.noch_zu_zahlen = 0
        self.rueckgeld = 0

    # die Methode __del__()
    def __del__(self):
        print("Eine Instanz der Klasse Muenzeinheit wurde gelöscht.")
        
    # die weiteren Methoden
    def muenzen_annehmen(self, wert):
        self.noch_zu_zahlen = self.noch_zu_zahlen - wert

    def rueckgeld_geben(self):
        # den absoluten Betrag von noch_zu_zahlen als Rückgeld liefern
        self.rueckgeld = abs(self.noch_zu_zahlen)
        return self.rueckgeld

    def set_betrag(self, preis):
        self.betrag = preis
        self.noch_zu_zahlen = self.betrag

    def get_noch_zu_zahlen(self):
        return self.noch_zu_zahlen

# die Vereinbarung der Klasse für den Automaten
class Getraenkeautomat:
    
    # übergeben werden Referenzen auf die Münzeinheit
    def __init__(self, temp_zahlomat):
        self.zahlomat = temp_zahlomat
        # Eine Liste für die Getränke
        self.getraenke_liste = []

        # die Getränke erzeugen
        getraenk = self.getraenke_liste.append(Getraenk("Wasser", 5, 2))
        getraenk = self.getraenke_liste.append(Getraenk("Limonade", 6, 20))
        getraenk = self.getraenke_liste.append(Getraenk("Bier", 7, 10))
        getraenk = self.getraenke_liste.append(Getraenk("Red-Bull", 8, 0))
    
        # die Kühlung ist aus
        self.kuehlung = False

    def __del__(self):
        print("Eine Instanz der Klasse Getraenkeautomat wurde gelöscht.")
        
    # MEIN VERSUCH MIT FEHLER
    def set_anzahl_flaschen(self, getraenk):
        self.getraenke_liste[getraenk].get_anzahl_flaschen() = self.getraenke_liste[getraenk].get_anzahl_flaschen() - 5
    
    def getraenke_waehlen(self):
        # die Auswahl
        print("Bitte wählen sie ein Gertänk:")
        print("Es gibt folgende Auswahl:")

        # 1. Möglichkeit
        anzeige_auswahl = 1
        for getraenk in self.getraenke_liste:
            print(anzeige_auswahl, getraenk.get_name_getraenk())
            anzeige_auswahl = anzeige_auswahl + 1

        # 2. Möglichkeit   
        anzeige_auswahl = 1    
        for getraenk in range(len(self.getraenke_liste)):
            print(anzeige_auswahl, self.getraenke_liste[getraenk].get_name_getraenk())
            anzeige_auswahl = anzeige_auswahl + 1

           
        auswahl = int(input("Geben Sie die gewünschte Nummer ein: "))
        auswahl -= 1
        
        # gibt es noch Flaschen vom gewählten Gertänk?
        if self.getraenke_liste[auswahl].get_anzahl_flaschen() == 0:
            print("Das gewählte Getränk ist leider nicht mehr vorhanden.")
            auswahl = - 1
            return auswahl

        
        anzahl = int(input("Wie viele Flaschen möchten Sie? "))

        if anzahl <= self.getraenke_liste[auswahl].get_anzahl_flaschen():
            self.getraenk_ausgeben(anzahl, auswahl)
        else:
            print("Es sind nur", self.getraenke_liste[auswahl].get_anzahl_flaschen(), "Flaschen", self.getraenke_liste[auswahl].get_name_getraenk(), "vorhanden.")
            print("Sie erhalten den Rest.")
            anzahl = self.getraenke_liste[auswahl].get_anzahl_flaschen()
            self.getraenk_ausgeben(anzahl, auswahl)
            
            
    def getraenk_ausgeben(self, anzahl, getraenke_index):
        # erst muss bezahlt werden
        print("Sie müssen", anzahl * self.getraenke_liste[getraenke_index].get_preis_pro_flasche(), "Cent bezahlen.")
        self.zahlomat.set_betrag(anzahl * self.getraenke_liste[getraenke_index].get_preis_pro_flasche())
        while self.zahlomat.get_noch_zu_zahlen() > 0:
            print("Es fehlen noch", self.zahlomat.get_noch_zu_zahlen(), "Cent.")
            self.zahlomat.muenzen_annehmen(3)
        # Geld zurückgeben
        print("Sie erhalten", self.zahlomat.rueckgeld_geben(),"Cent zurück,")
        # Flaschen ausgeben
        print("Sie erhalten", anzahl, "Flasche(n)", self.getraenke_liste[getraenke_index].get_name_getraenk())

        # DAS SIND MEINE VERSUCHE DIE ANAZHL ZU ÄNDERN
        self.set_anzahl_flaschen(getraenke_index)
        print(self.set_anzahl_flaschen(getraenke_index))
        print(self.getraenke_liste[getraenke_index].get_anzahl_flaschen())
        
    def kuehlen(self, an_aus):
        self.kuehlung = an_aus
        if self.kuehlung == True:
            print("Die Kühlung ist eingeschaltet.")
        else:
            print("Die Kühlung ist ausgeschaltet.")


# eine Münzeinheit erzeugen
zahlomat = Muenzeinheit()

# einen Automaten erzeugen
#die Münzeinheit wird übergeben
automat = Getraenkeautomat(zahlomat)


auswahl = - 1
# die Kühlung einschalten
automat.kuehlen(True)

# ein Getränk auswählen
while auswahl == -1:
    auswahl = automat.getraenke_waehlen()

# die Kühlung ausschalten
automat.kuehlen(False)

# die Instanzen ausdrücklich freigeben
del automat
del zahlomat
Ich dachte, dass die Instanzen als Mehrdimensionale Liste gespeichert wird, und ich auf die Werte einfach zugreifen und ändern kann, wie Sirius3 schreibt.
Sorry für die Grammatik, bin kein Deutscher :D
Sirius3
User
Beiträge: 18279
Registriert: Sonntag 21. Oktober 2012, 17:20

Du darfst gerne Deinen Lehrer hier vorbeischicken, da ist vieles dabei, was von schlechter Stil über falsch verstandenem Konzept bis schwerwiegender Fehler reicht.
Die falschen Getter wurden hier ja schon angesprochen. __del__ ist eine Methode, die man nie braucht, also auch nicht lernen muß. Und die beiden del-Zeilen zum Schluß gehören genauso ersatzlos gelöscht. Der Kommentar dabei zeugt von falschem Verständnis, wie der Lebenszyklus von Objekten in Python ist.

`getraenke_waehlen` ist der falsche Name für die Methode, bzw. umgekehrt, die Methode macht viel zu viel, nämlich nicht nur ein Getränk auswählen, sondern auch noch die ganze Zahlungsabwicklung.
Der Rückgabewert ist sehr komisch, um nicht zu sagen falsch. -1 bei falscher Eingabe und None bei Erfolg. Richtig wäre hier ein Boolean.
Die Ausgabe der Getränkenamen gehört in eine eigene Methode, Möglichkeit 2 ist zu nicht empfehlenswert, Möglichkeit 1 ist nur bedingt gut:

Code: Alles auswählen

class Getraenkeautomat:
    def __init__(self, zahlomat):
        self.zahlomat = zahlomat
        self.getraenke = [
            Getraenk("Wasser", 5, 2),
            Getraenk("Limonade", 6, 20),
            Getraenk("Bier", 7, 10),
            Getraenk("Red-Bull", 8, 0),
        ]

    def getraenke_anzeigen(self):
        for nr, getraenk in enumerate(self.getraenke, 1):
            print(f"{nr}: {getraenk.name}")
`self.getraenke_liste[auswahl].get_anzahl_flaschen()` steht vier mal im Code, was diesen sehr lang und schwer zu lesen macht.
Da der Getränkeautomat eh schon so viele Sachen macht, kann man die while-Schleife auch gleich in die Methode verschieben. Dann braucht es den Rückgabewert erst gar nicht.
Übrigens, wenn man erst eine Variable mit einem Dummy-Wert initialisieren muß, damit eine while-Schleife startet, schreibt man statt dessen eine while-True-Schleife.

Code: Alles auswählen

    def getraenk_waehlen(self):
        while True:
            print("Bitte wählen sie ein Gertänk:")
            print("Es gibt folgende Auswahl:")
            self.getraenke_anzeigen()

            # TODO: Eingabe prüfen
            auswahl = int(input("Geben Sie die gewünschte Nummer ein: ")) - 1
            getraenk = self.getraenke[auswahl]
            maximale_anzahl = getraenk.anzahl_flaschen
            if maximale_anzahl == 0:
                print("Das gewählte Getränk ist leider nicht mehr vorhanden.")
            else:
                break

        anzahl = int(input("Wie viele Flaschen möchten Sie? "))
        if anzahl > maximale_anzahl:
            print("Es sind nur {maximale_anzahl} Flaschen {getraenk.name} vorhanden.")
            print("Sie erhalten den Rest.")
            anzahl = maximale_anzahl
        return getraenk, anzahl
Jetzt fehlt noch die Methode zum Zahlen, die Du nach dem selben Muster umschreiben solltest.
Zum Schluß muß das Hauptprogramm noch in eine Funktion wandern, weil man sonst globale Variablen erzeugt, die es nicht geben sollte:

Code: Alles auswählen

def main():
    zahlomat = Muenzeinheit()
    automat = Getraenkeautomat(zahlomat)
    automat.kuehlen(True)
    getraenk, anzahl = automat.getraenk_waehlen()
    automat.zahlen_und_ausgeben(getraenk, anzahl) # TODO: noch zu implementieren
    automat.kuehlen(False)

if __name__ == "__main__":
    main()
Wie schon oben geschrieben, kannst Du die Anzahl direkt über das Attribut getraenke.anzahl_flaschen verändern.
Brpo25
User
Beiträge: 7
Registriert: Donnerstag 6. Januar 2022, 11:17

Es ist krass, wie das bei dir einfach aussieht. So wurde ich das auch gerne lernen, halt muss das machen, was in dem Studienheft steht und was der Lehrer sagt. (Der hat eigentlich die alle Studienhefte geschrieben) Es sind jetzt eigentlich die Grundlagen von Informatik und ab und zu kommt auch etwas mit Python. Und das ist, meine Meinung nach, nicht richtig Erklärt. Aber was kann ich machen. Ich kann jetzt nicht dem Lehrer sagen, guck Mal, so ist das richtig von Profis geschrieben.
Werde das jetzt alles zusammen schreiben, und mir alles notieren, was ihr mir hier gesagt habt.
Vielen Dank für ihre Hilfe.

PS: Kann sein, das ich mehr Mal vorbei komme haha
Brpo25
User
Beiträge: 7
Registriert: Donnerstag 6. Januar 2022, 11:17

Ich habe das fertig gemacht, habe aber noch zwei Fragen zu dem Code

Code: Alles auswählen

class Getraenk:
    def __init__(self, name, preis, flaschenanzahl):
        self.name = name
        self.preis = preis
        self.flaschenanzahl = flaschenanzahl

class Muenzeinheit:
    def __init__(self):
        self.betrag = 0
        self.noch_zu_zahlen = 0
        self.rueckgeld = 0

    def muenzen_annehmen(self, wert):
        self.noch_zu_zahlen = self.noch_zu_zahlen - wert

    def rueckgeld_geben(self):
        self.rueckgeld = abs(self.noch_zu_zahlen)
        return self.rueckgeld

    def set_betrag(self, preis):
        self.betrag = preis
        self.noch_zu_zahlen = self.betrag

    def get_noch_zu_zahlen(self):
        return self.noch_zu_zahlen
    
class Getraenkeautomat:
    def __init__(self, zahlomat):
        self.zahlomat = zahlomat
        self.getraenke = [
            Getraenk("Wasser", 5, 2),
            Getraenk("Limonade", 6, 20),
            Getraenk("Bier", 7, 10),
            Getraenk("Red-Bull", 8, 0)
        ]

    def getraenke_anzeige(self):
        for nr, getraenk in enumerate(self.getraenke, 1):
            print(f"{nr}: {getraenk.name}")

    def getraenk_waehlen(self):
        while True:
            print("Bitte wählen Sie ein Getränk: ")
            print("Es gibt folgende Auswahl: ")
            self.getraenke_anzeige()

            # TODO
            auswahl = int(input("Geben sie die gewünschte Nummer ein: ")) - 1
            getraenk = self.getraenke[auswahl]
            maximale_anzahl = getraenk.flaschenanzahl
            if maximale_anzahl == 0:
                print("Das gewählte Getränk ist leider nichr mehr vorhanden.")
            else:
                break

        anzahl = int(input("Wie ciele Flaschen möchten Sie? "))
        if anzahl > maximale_anzahl:
            print(f"Es sind nur {maximale_anzahl} Flaschen {getraenk.name} vorhanden.")
            print("Sie erhalten den Rest.")
            anzahl = maximale_anzahl
        return getraenk, anzahl
    
    def kuehlen(self, an_aus):
        self.kuehlung = an_aus
        if self.kuehlung == True:
            print("Die Kühlung ist eingeschaltet.")
        else:
            print("Die Kühlung ist ausgeschaltet.")
            
    def zahlen_und_ausgeben(self, getraenk, anzahl):
        print("Sie müssen", anzahl * getraenk.preis, "Cent bezahlen.",
              self.zahlomat.set_betrag(anzahl * getraenk.preis))
        while self.zahlomat.get_noch_zu_zahlen() > 0:
            print("Es fehlen noch", self.zahlomat.get_noch_zu_zahlen(), "Cent.")
            self.zahlomat.muenzen_annehmen(3)
        print("Sie erhalten", self.zahlomat.rueckgeld_geben(),"Cent zurück.")
        print("Sie erhalten", anzahl, "Flaschen(n)", getraenk.name)
        getraenk.flaschenanzahl -= anzahl
        

def main():
    zahlomat = Muenzeinheit()
    automat = Getraenkeautomat(zahlomat)
    automat.kuehlen(True)
    getraenk, anzahl = automat.getraenk_waehlen()
    automat.zahlen_und_ausgeben(getraenk, anzahl) #TODO
    automat.kuehlen(False)
    
if __name__ == "__main__":
    main()
1: Warum ist das Abrufen der Main Methode so gemacht?

Code: Alles auswählen

if __name__ == "__main__":
    main()
2: Ist hier die Datenkapselung eingehalten? Ich greife auf die Attribute von den erzeugten Getränken, die sind aber in der Liste in der Getraenkeautomat Klasse gespeichert. Also ich greife auf die in der selber Klasse. Verstehe ich das richtig? Oder wie ist das eigentlich damit? Wir lerne, dass ein Objekt sollte niemals direkt auf die Eigenschaften eines anderen Objekt zugreifen, sondern nur Methode aufrufen, die dann die Eigenschaft verändern.
Sirius3
User
Beiträge: 18279
Registriert: Sonntag 21. Oktober 2012, 17:20

Sieht doch jetzt ganz gut aus.

Zu 1) irgendwie muß die main-Funktion aufgerufen werden, aber nur, wenn es sich wirklich um ein Skript handelt, und nicht, wenn diese Datei als Modul verwendet wird. Und das if macht diese Unterscheidung.

Zu 2) Das Konzept Datenkapselung bedeutet, dass man zwischen Sachen, die Klassen-intern sind und solchen, auf die man von außen darauf zu greifen darf, klar unterscheidet. In Python macht man die Unterscheidung, dadurch, dass interne Attribute und Methoden mit einem _ anfangen.
In anderen Programmiersprachen gibt es Schlüsselwörter wie ›public‹, ›private‹, etc.

Diese unsägliche Meinung, dass man Setter und Getter schreiben muß, kommt aus einer anderen Programmiersprache (Java), dort hat das aber nichts mit Datenkapselung zu tun, sondern mit der Tatsache, dass es keine Properties gibt, also Getter- und Setter-Methoden, die sich wie Attribute verhalten.
Man könnte auch sagen, dass

Code: Alles auswählen

getraenk.flaschenanzahl -= anzahl
das selbe ist wie

Code: Alles auswählen

getraenk.set_flaschenanzahl(getraenk.get_flaschenanzahl() - anzahl)
nur dass Python automatisch dafür sorgt, dass für triviale Fälle set_flaschenanzahl und get_flaschenanzahl implementiert und die Schreibweise bei der Benutzung viel angenehmer ist.

Nicht-triviale Setter und Getter implementiert man in Python durch Properties:

Code: Alles auswählen

class Getraenk:
    def __init__(self, name, preis, flaschenanzahl):
        self.name = name
        self.preis = preis
        self._flaschenanzahl = flaschenanzahl

    @property
    def flaschenanzahl(self):
        return self._flaschenanzahl
        
    @flaschenanzahl.setter
    def flaschenanzahl(self, anzahl):
        if anzahl < 0:
            raise ValueError("Anzahl darf nicht negativ sein")
        self._flaschenanzahl = anzahl
Hier wird jetzt beim Setzen von ›getraenk.flaschenanzahl‹ geprüft, dass die Anzahl nicht negativ sein darf.
Brpo25
User
Beiträge: 7
Registriert: Donnerstag 6. Januar 2022, 11:17

Ach OK. Manche Sachen verstehe ich noch nicht richtig, aber das kommt. Hoffentlich haha.
Ich wurde jetzt den Quelltext von dir dem Lehrer zeigen um zu sehen, was er dazu sagt, ob die Aufgabe erfüllt ist. Bin gespannt ob ihm das gefallen wurde.
Ich danke dir(euch) und werde mich noch ein mal melden, wenn ich die Antwort bekomme.
Brpo25
User
Beiträge: 7
Registriert: Donnerstag 6. Januar 2022, 11:17

Also, laut mein Lehrer, ist die Umsetzung in Ordnung, bis auf die fehlende Datenkapselung.
Keine Ahnung warum müssen wir das so machen, wenn das so in normalen Leben nicht ist.
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Brpo25: Weil der Lehrplan Sachen für wichtig erachtet, die in Python nicht üblich sind, aber trotzdem die Entscheidung getroffen wurde das mit Python zu unterrichten. Ziemlich sicher sind die Entscheidungsträger für so etwas keine Python-Programmierer; wahrscheinlich nicht einmal überhaupt Programmierer. Und selbst wenn sie das wussten, könnte es ein Kompromiss gewesen sein. Andererseits hätten sie auch Ruby als Programmiersprache nehmen können.

Das Problem ist, dass ihr ja nicht Python lernen sollt, sondern Programmieren allgemein. Und da gehört in der Regel auch Zugriffsschutz dazu, weil es verbreitete Programmiersprachen gibt, die das Konzept kennen und unterstützen. Python kennt das nicht. Also ist es immer etwas problematisch das damit irgendwie erklären zu wollen.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Brpo25
User
Beiträge: 7
Registriert: Donnerstag 6. Januar 2022, 11:17

Ja verstehe, hast du recht.
Sirius3
User
Beiträge: 18279
Registriert: Sonntag 21. Oktober 2012, 17:20

@__blackfack__: Gestern und Setter haben ja nicht wirklich was mit Datenkapselung zu tun. Vor allem nicht, wenn sie so trivial sind.
Das Konzept kann man an guten Beispielen auch in Python erklären, ohne dass man gegen die Sprache arbeitet.
Hier wird aber nur ein (unbrauchbares) Muster vermittelt und nicht das Konzept.
Antworten