Meine eigene Klasse kennt sich selbst nicht

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
kopython
User
Beiträge: 2
Registriert: Freitag 22. April 2022, 14:39

Das ist meine Klasse:
""" Class definition for a citrus plant. """

import random

class Citrus:

def __init__(self, species: str = None, possible_species = ("Pomelo", "Mandarin", "Citron")):
if species == None:
self.species = random.choice(possible_species)
else:
self.species = species

def __str__(self):
return "<Citrus of species " + self.species + ">"

def __add__(self, other):
if not isinstance(type(self), type(Citrus) ) or not isinstance(type(other), type(Citrus)):
raise TypeError("plant not excepted. Both plants should be of type Citrus")
elif self.species == other.species:
return Citrus(self.species)
elif (self.species == "Pomelo" and other.species == "Mandarin") or (self.species == "Mandarin" and other.species == "Pomelo"):
return random.choice([Citrus("Sweet Orange"), Citrus("Tangerine"), Citrus("Satsuma"), Citrus("Wildleaf Mandarin"), Citrus("Bitter Orange")])
elif (self.species == "Pomelo" and other.species == "Sweet Orange") or (self.species == "Sweet Orange" and other.species == "Pomelo"):
return Citrus("Grapefruit")
elif (self.species == "Wildleaf Mandarin" and other.species == "Sweet Orange") or (self.species == "Sweet Orange" and other.species == "Wildleaf Mandarin"):
return Citrus("Clementine")
elif (self.species == "Citron" and other.species == "Bitter Orange") or (self.species == "Bitter Orange" and other.species == "Citron"):
return Citrus("Lemon")
else:
return random.choice([Citrus(self.species), Citrus(other.species)])


def __radd__(self):
raise TypeError("plant not excepted. Both plants should be of type Citrus")


Fehlermeldung:

File "/home/.../citrus.py", line 20, in __add__
elif self.species == other.species:
AttributeError: type object 'Citrus' has no attribute 'species'

Die kommt beim verwenden der Add-Methode, was mach ich falsch?
LG
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kopython: Du übergibst da offenbar etwas falsches. Wäre ja nett gewesen zu sehen *was*.

Die erste ``if``-Bedingung in `__add__()` ist falsch. Die `type()`-Aufrufe haben da so überhaupt nichts zu suchen.

Und wenn man das korrigiert ist die erste Teilbedingung unsinnig, denn `self` ist *immer* ein `Citrus`-Exemplar, denn das ist ja `Citrus.__add__()`.

Die Regeln die danach folgen würde ich als Datenstruktur formulieren statt mit so viel Code. Und vielleicht nicht so verdammt viele `Citrus`-Objekte erstellen die gleich darauf wieder verworfen werden.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
ThomasL
User
Beiträge: 1379
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Ich habe das mal etwas verändert und bei mir läuft es so:

Code: Alles auswählen

import random

variants = {("Pomelo", "Mandarin") : ("Sweet Orange", "Tangerine", "Satsuma", "Wildleaf Mandarin", "Bitter Orange"),
            ("Mandarin", "Pomelo") : ("Sweet Orange", "Tangerine", "Satsuma", "Wildleaf Mandarin", "Bitter Orange"),
            ("Pomelo", "Sweet Orange") : ("Grapefruit", ),
            ("Sweet Orange", "Pomelo") : ("Grapefruit", ),
            ("Wildleaf Mandarin", "Sweet Orange") : ("Clementine", ),
            ("Sweet Orange", "Wildleaf Mandarin") : ("Clementine", ),
            ("Citron", "Bitter Orange") : ("Lemon", ),
            ("Bitter Orange", "Citron") : ("Lemon", ),
            }

class Citrus:

    def __init__(self, species = None, possible_species = ("Pomelo", "Mandarin", "Citron")):
        self.species = species or random.choice(possible_species)

    def __str__(self):
        return "<Citrus of species " + self.species + ">"

    def __repr__(self):
        return "<Citrus of species " + self.species + ">"

    def __add__(self, other):
        if not isinstance(other, Citrus):
            raise TypeError("plant not excepted. Both plants should be of type Citrus")
        
        if self.species == other.species:
            return Citrus(self.species)
            
        try:
            return Citrus(random.choice(variants[(self.species, other.species)]))
        except KeyError:
            return Citrus(random.choice([self.species, other.species]))

    def __radd__(self):
        raise TypeError("plant not excepted. Both plants should be of type Citrus")
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Sirius3
User
Beiträge: 18279
Registriert: Sonntag 21. Oktober 2012, 17:20

Wieder so ein Fall, wo die Typannotation offensichtlich falsch ist, da der Defaultwert von `species` kein String ist.
`possible_species` wäre wohl besser eine Klassenkonstante.
Auf None testet man mit ›is‹.
Statt Strings mit + zusammenzustückeln benutzt man Format-Strings.
Bei __radd__ fehlt das zweite Argument. Aber wenn es eh nicht implementiert ist, kann man das ja auch weglassen.

Code: Alles auswählen

import random

class Citrus:
    POSSIBLE_SPECIES = ["Pomelo", "Mandarin", "Citron"]
    def __init__(self, species=None):
        if species is None:
            species = random.choice(self.POSSIBLE_SPECIES)
        self.species = species

    def __str__(self):
        return f"<Citrus of species {self.species}>"
    
    def __add__(self, other):
        if not isinstance(other, Citrus):
            raise TypeError("plant not excepted. Both plants should be of type Citrus")
        elif self.species == other.species:
            return Citrus(self.species)
        elif set(self.species, other.species) == {"Pomelo", "Mandarin"}:
            return Citrus(random.choice(["Sweet Orange", "Tangerine", "Satsuma", "Wildleaf Mandarin", "Bitter Orange"]))
        elif set(self.species, other.species) == {"Pomelo", "Sweet Orange"}:
            return Citrus("Grapefruit")
        elif set(self.species, other.species) == {"Wildleaf Mandarin", "Sweet Orange"}:
            return Citrus("Clementine")
        elif set(self.species, other.species) == {"Citron", "Bitter Orange"}:
            return Citrus("Lemon")
        else:
            return Citrus(random.choice([self.species, other.species]))
Und ohne den Code, der die Klasse aufruft, kann man nichts zum Fehler sagen.
kopython
User
Beiträge: 2
Registriert: Freitag 22. April 2022, 14:39

Vielen Dank schonmal! Ich habe den etwas schickeren Code übernommen aber hänge damit jetzt an der nächsten Stelle fest.
Der gesamte Code:

Code: Alles auswählen

 """ Class definition for a citrus plant. """

import random

variants = {("Pomelo", "Mandarin") : ("Sweet Orange", "Tangerine", "Satsuma", "Wildleaf Mandarin", "Bitter Orange"),
            ("Mandarin", "Pomelo") : ("Sweet Orange", "Tangerine", "Satsuma", "Wildleaf Mandarin", "Bitter Orange"),
            ("Pomelo", "Sweet Orange") : ("Grapefruit", ),
            ("Sweet Orange", "Pomelo") : ("Grapefruit", ),
            ("Wildleaf Mandarin", "Sweet Orange") : ("Clementine", ),
            ("Sweet Orange", "Wildleaf Mandarin") : ("Clementine", ),
            ("Citron", "Bitter Orange") : ("Lemon", ),
            ("Bitter Orange", "Citron") : ("Lemon", ),
            }

class Citrus:

    def __init__(self, species = None, possible_species = ("Pomelo", "Mandarin", "Citron")):
        self.species = species or random.choice(possible_species)

    def __str__(self):
        return "<Citrus of species %s >"% (self.species)

    def __repr__(self):
        return "<Citrus of species %s >"% (self.species)

    def __add__(self, other):
        if not isinstance(other, Citrus):
            raise TypeError("plant not excepted. Both plants should be of type Citrus")
        
        if self.species == other.species:
            return Citrus(self.species)
            
        try:
            return Citrus(random.choice(variants[(self.species, other.species)]))
        except KeyError:
            return Citrus(random.choice([self.species, other.species]))

    def __radd__(self):
        raise TypeError("plant not excepted. Both plants should be of type Citrus")

Code: Alles auswählen

""" Class definition for a garden with plants. """

from citrus import Citrus

import random


class Garden:

    def __init__(self, plants: list = []) -> None:
        self.plants = plants

    
    def __len__(self):
        return len(self.plants)

    def __str__(self):
        return "<Garden with " + str(len(self.plants)) + " plants and " + str(len(set(self.plants))) + " species> "
       

    def plant(self, new_plant = None):
        if new_plant == None :
            self.plants.append(Citrus)
        else:
            self.plants.append(new_plant)

    def cross(self):
        if len(self.plants) >= 2:
            crossed_citrus = random.choice(self.plants) + random.choice(self.plants)
            self.plant(crossed_citrus)

Code: Alles auswählen

""" Script to simulate a garden full of citrus plants. """

from garden import Garden
from citrus import Citrus


def main():

    print("\n" + "#" * 20, "Simulating Citrus", "#" * 20) 
    simulate_citrus()

    print("\n" + "#" * 20, "Simulating Garden", "#" * 20) 
    simulate_garden()


def simulate_citrus():
    """ Simulates instances of class Citrus. """

    print("\nCreating a Citrus my_citrus ...")
    my_citrus = Citrus()

    print("\nPrinting my_citrus...")
    print(my_citrus)

    print("\nCreating a Citrus other_citrus ...")
    other_citrus = Citrus()

    print("\nPrinting other_citrus ...")
    print(other_citrus)

    print("\nPrinting my_citrus + other_citrus ...")
    print(my_citrus + other_citrus)


def simulate_garden():
    """ Simulates an instance of class Garden. """

    print("\nCreating a Garden my_garden ...")
    my_garden = Garden()

    print(my_garden)

    print("\nPlanting 5 Citrus plants ...")
    for _ in range(5):
        my_garden.plant()

    print(my_garden)

    print("\nCrossing plants in my_garden 20 times ...")
    for _ in range(20):
        my_garden.cross()

    print(my_garden)

    print("\nThe following species now exist in the garden:", {plant.species for plant in my_garden.plants}, "\n")


if __name__ == "__main__":
    main()

Mit folgender Fehlermeldung:

Traceback (most recent call last):
File ".../simulate.py", line 59, in <module>
main()
File ".../simulate.py", line 13, in main
simulate_garden()
File ".../simulate.py", line 51, in simulate_garden
my_garden.cross()
File ".../garden.py", line 29, in cross
crossed_citrus = random.choice(self.plants) + random.choice(self.plants)
TypeError: unsupported operand type(s) for +: 'type' and 'type'
Benutzeravatar
ThomasL
User
Beiträge: 1379
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Nun, scheinbar funktioniert

Code: Alles auswählen

crossed_citrus = random.choice(self.plants) + random.choice(self.plants)
nicht.

Die Frage dabei ist, was ist denn in self.plants enthalten? Der Fehler (den ich gesehen habe) liegt in Garden.plant().
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Sirius3
User
Beiträge: 18279
Registriert: Sonntag 21. Oktober 2012, 17:20

@kopython: man schreibt nicht pro Klasse ein eigenes Modul. Im jetzigen Zustand macht mehr als eine einzige Datei noch keinen Sinn.
In Garden.__init__ benutzt Du eine Liste als Defaultargument. Das ist ein schwer zu findender Fehler, denn es existiert nur eine einzige Liste für alle Garden-Instanzen. Man muß in __init__ für jede Instanz eine eigene Liste erzeugen.
Und wie ich schon geschrieben hatte, werden Strings nicht mit + zusammengestückelt, sondern man benutzt Format-Strings. Mit None wird per ›is None‹ verglichen.
Garden.plant fügt die Citrus-Klasse in self.plants ein, das ist falsch, Du willst ja eine Instanz hinzufügen.
In Garden.cross machst Du einfach nichts, wenn nicht genug Pflanzen gepflanzt worden sind; einfach nichts zu tun, ist aber oft nicht gut, weil damit Fehler schwer zu entdecken sind.
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kopython: Die `__str__()`-Implementierungen sollten weg bzw. `__repr__()`-Implementierungen sein. Die vom `Garden` hat einen Fehler weil auch `Citrus`-Objekte mit dem gleichen `species`-Wert zwei Elemente in einem `set` sind und nicht nur einer. ``str(len(self.plants))`` ist auch unnötig umständlich wenn `self` doch selbst schon als Argument für `len()` verwendet werden kann.

Die Vorsilben `my_` und `new_` machen nicht wirklich Sinn und können weg.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten