TypeError in Zusammenhang mit Decorator Pattern

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
pizza-Baecker
User
Beiträge: 2
Registriert: Mittwoch 28. Dezember 2022, 13:52

hallo zusammen

Ich habe mehrere via Decorator Pattern verküpfte Klassen und ein TypeError-Problem. Die Strings werden in Zeile 24 und 42 wie gewünscht konkateniert, aber ich erhalte dennoch die Fehlermeldung "TypeError: sequence item 0: expected str instance, method found". Aber ich habe doch hier gar keine Methode? Ich vermute, dass mein Problem etwas mit Sichtbarkeit zu tun hat; mein String ist in __init__ von Belag sichtbar, aber nicht ausserhalb. Aber so richtig Sinn macht das auch nicht; der Job vom __init__ ist es doch gerade, von ausserhalb der Klasse nutzbare Objekte zu erstellen.
Also; ich blick' nicht mehr durch.
Bitte helft mir!
Ich danke im Voraus für alle Hinweise.
Liebe Grüsse aus Bern
Markus

Code: Alles auswählen

# Basisklasse für Pizza
class Pizza:
    def __init__(self):
        self.beschreibung = "Pizza"

    def beschreibung(self):
        return self.beschreibung

    def wähle_Komponente(self, komponente):
        for i, element in enumerate(komponente):
            print(f"{i + 1}. {element}")
        nummer = int(input("Bitte wähle eine Nummer aus der Liste aus: ")) - 1
        return str(komponente[nummer])

# Klasse für den "Decorator" Belag
class Belag(Pizza):
    verfügbareBeläge=["Peperoni","Käse","Schinken"]

    def __init__(self, pizza):
        print ("__init_ Belag")
        self.pizza = pizza
        print(self.pizza.beschreibung)
        self.pizza.beschreibung = self.pizza.wähle_Komponente(Belag.verfügbareBeläge),"-",self.pizza.beschreibung
        self.pizza.beschreibung = "".join(self.pizza.beschreibung)

        print (self.pizza.beschreibung)
        print("Komponente fertig\n")

    def beschreibung(self):
        return self.pizza.beschreibung


# Klasse für den "Decorator" Teig
class Teig(Pizza):
    verfügbareTeige=["Vollkorn","Dinkel","glutenfrei"]

    def __init__(self, pizza):
        print ("__init_ Teig")
        self.pizza = pizza
        print(self.pizza.beschreibung)
        self.pizza.beschreibung = self.pizza.beschreibung," mit ",(self.pizza.wähle_Komponente(Teig.verfügbareTeige))+"-Teig" 
        self.pizza.beschreibung = "".join(self.pizza.beschreibung)

        print (pizza.beschreibung)
        print("Komponente fertig\n")

    def beschreibung(self):
        return self.pizza.beschreibung

# In dieser Klasse wird die Bestellung beschrieben
class Bestellung:
    def __init__(self):
        self.bestellung = []

    def komponiere_pizza(self):
        # Erstelle eine "leere" Pizza
        print("\nErstelle eine leere Pizza")
        pizza = Pizza()
        print(pizza.beschreibung)

        # Belegen der Pizza
        print("\nBelegen der Pizza")
        pizza = Belag(pizza)
        print(pizza.beschreibung) 

        # Auswahl des Teigs
        print("\nAuswahl des Teigs")
        pizza = Teig(pizza)
        print(pizza.beschreibung) 

        # Füge die aktuelle Pizza der Bestellung hinzu
        self.bestellung.append(pizza)

        # weitere Pizzen bestellen

    def zeigeBestellung (self):
        print ("Du hast folgende Produkte bestellt:\n")
        for produkt in self.bestellung:
            print (produkt.beschreibung)


###TEST###
testBestellung = Bestellung()
testBestellung.komponiere_pizza()
testBestellung.zeigeBestellung()
Sirius3
User
Beiträge: 18276
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Klassen machen so keinen Sinn. Ein Belag ist keine Pizza. Und ein Teig ist auch keine Pizza. Eine Pizza hat einen Teig und einen Belag. Oh, der Belag ist nicht nur eine Pizza, sondern hat auch noch eine. Dein ganzes Klassendesign musst Du nochmal überdenken und umdrehen.
`beschreibung` ist eine Methode, die Du aber mit einen Attribut gleichen Namens überdeckst.
Benutzeravatar
__blackjack__
User
Beiträge: 14069
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@pizza-Baecker: Da ist auch unschön die Benutzerinteraktion in den Klassen der Programmlogik. Eine Pizza fragt zum Beispiel keinen Benutzer/Kunden nach Belägen. Und diese ”Methode” ist auch überhaupt keine Methode weil die nichts von dem `Pizza`-Objekt braucht. Das könnte man ganz einfach als Funktion ausserhalb der `Pizza`-Klasse schreiben ohne das sich was ändert ausser das das nicht verwendete `self` weg fällt.

`komponiere_pizza()` ist auf `Bestellung`-Objekten IMHO auch deplaziert.

Diese `verfügbare…`-Klassenattribute sind auch ein bisschen komisch. Warum sollte man das statisch in der Klasse festlegen wollen?

Was in der Regel ein starkes Warnzeichen ist, sind Attribute die den gleichen Namen wie die Klasse tragen. Eine Klasse beschreibt ein ”Ding” im weitesten Sinne und die Attribute beschreiben woraus sich der Zustand von diesem ”Ding” zusammensetzt. Der Satz „<Klassenname> besteht aus <Attributnamen> [und <Attributname]*“ sollte schlüssig klingen. Das tut „Eine Bestellung besteht aus bestellung“ nicht. Das Attribut sollte `posten` (Mehrzahl) oder `pizzen` heissen (falls man wirklich nur Pizzen bestellen kann, und keine Beilagen, Vorspeisen, Nachtische, Getränke, …).

Statt Tupel aus Zeichenketten zu erzeugen die dann mit ``"".join(…)`` zusammengesetzt werden, würde man Zeichenkettenformatierung mit der `format()`-Methode oder f-Zeichenkettenliterale verwenden.

Die `beschreibung()` scheint mir ein guter Kandidat für eine `__str__()`-Methode zu sein.

Du verwendest hier den Begriff „Decorator“ als Entwurfsmuster. (Python hat auch ein Syntaxkonstrukt das „decorator“ heisst.) Mit Entwurfsmustern löst man ja Probleme. Beschreib doch mal welches Problem Du hier mit dem Entwurfsmuster lösen willst? Nicht dass das hier ein Fall von „Ich habe ein Entwurfsmuster und suche ein Problem dafür“ ist. Das gehört umgekehrt: Man hat ein Problem und schaut dann ob es ein Entwurfsmuster gibt, was das lösen könnte.

Falls Dir darauf keine Antwort einfallen sollte, schreib das ganze mal ohne Entwurfsmuster (im Hinterkopf) und zeig das mal, und demonstriere oder erkläre zumindest das Problem welches das Entwurfsmuster „Decorator“ bei dem konkreten Code dann löst.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Ich würde auch Abstand von Umlauten in Namen nehmen. Ja, Python kann das, aber auch heute leider auch immer noch nicht jedes Werkzeug das ansonsten mit Python- oder Quelltext allgemein umgehen kann. Die Namen selbst würde ich auch in Englisch wählen. Das passt besser zum gesamten Umfeld (Sprache, Bibliotheken, Dokumentation, …) und bei der Benennung von Containertypen hat man in Deutsch sehr oft das Problem, dass Einzahl und Mehrzahl gleich geschrieben werden. Beispielsweise bei Bestellung und `posten` weiss man nicht ob das nun für einen Posten steht oder für mehrere Posten. Das Problem hat man im Englischen fast nie.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
pizza-Baecker
User
Beiträge: 2
Registriert: Mittwoch 28. Dezember 2022, 13:52

@__blackjack__: Vielen Dank für die ausführliche Analyse meines Codes!
Ich habe davon eines ausprobiert und dann festgestellt, dass ich mich hier wohl in etwas verrannt habe. Ich habe nun nochmal von vorne angefangen und einen anderen Ansatz gewählt.
Das Decorator Pattern habe ich übrigens gewählt, weil ich die Pizza-Klasse erweitern wollte ohne Veränderung der Klasse selbst und der Funktionen, die darauf zugreifen. Aber dieses Szenario passt da nicht wirklich gut.
Auf jeden Fall hab' ich wieder dazugelernt, auch dank deiner Hilfe!
Ein schönes 2023!
Antworten