Seite 1 von 1

Meine eigene Klasse kennt sich selbst nicht

Verfasst: Freitag 22. April 2022, 14:50
von kopython
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

Re: Meine eigene Klasse kennt sich selbst nicht

Verfasst: Freitag 22. April 2022, 15:01
von __blackjack__
@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.

Re: Meine eigene Klasse kennt sich selbst nicht

Verfasst: Freitag 22. April 2022, 16:45
von ThomasL
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")

Re: Meine eigene Klasse kennt sich selbst nicht

Verfasst: Samstag 23. April 2022, 18:46
von Sirius3
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.

Re: Meine eigene Klasse kennt sich selbst nicht

Verfasst: Sonntag 24. April 2022, 12:04
von kopython
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'

Re: Meine eigene Klasse kennt sich selbst nicht

Verfasst: Sonntag 24. April 2022, 13:05
von ThomasL
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().

Re: Meine eigene Klasse kennt sich selbst nicht

Verfasst: Sonntag 24. April 2022, 13:31
von Sirius3
@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.

Re: Meine eigene Klasse kennt sich selbst nicht

Verfasst: Sonntag 24. April 2022, 16:46
von __blackjack__
@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.