@Kusja: Okay, ich bin ein bisschen zu spät dran mit folgenem, aber ich wollt's auch nicht wegwerfen.
Man kann nicht so einfach beliebigen Code der Vererbung verwendet so umschreiben das er Komposition verwendet. Jedenfalls nicht sinnvoll. Es hat ja einen Grund warum es diese beiden Konzepte gibt. Vererbung ist eine „ist ein(e)“-Beziehung und Komposition ist eine „hat ein(e)“-Beziehung zwischen zwei Klassen.
Anmerkungen zum Code: Die Kommentare sind fast alle überflüssig. Kommentare sollen den Leser einen Mehrwert liefern. ``# Kindklasse`` vor Klassendefinitionen schreiben die offensichtlich eine Kindklasse sind, weil sie von einer Elternklasse abgeleitet werden, bringt dem Leser nichts. Genau so wenig wie der Hinweis ``#Tiere hinzufügen`` vor Zeilen in denen ganz offensichtlich Tiere zu einem Tierheim hinzufügt werden. Faustregel: Nicht kommentieren *was* der Code macht, denn das steht da bereits als Code, sondern warum er das so macht. Sofern das nicht offensichtlich ist.
Der Kommentar direkt nach der Definition der `Tierheim` gehört zu welchem Code? Kommentare die alleine auf Zeilen stehen, also nicht hinter Code in einer Zeile, beziehen sich auf den Code nach dem Kommentar.
Wenn eine Klasse nicht von einer anderen abgeleitet wird, braucht man auch keine leeren Klammern um die nicht-angegebene Elternklasse zu setzen.
Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).
Der Klassenname selbst sollte sich in der Regel nicht noch mal in seinen Attributen wiederfinden, denn der Klassename setzt ja bereits den Kontext für den Attributnamen. Bei einem `Tierheim` ist klar das ein Attributname `name` den Namen des Tierheims darstellt, da muss man das Attribut dann nicht `tierheim_name` nennen.
Was soll das Attribut `Tierheim.Tier` bedeuten? Das macht keinen Sinn. Du übergibst da `vogel` beim Erstellen. Warum? Warum ist dieser Vogel am Ende sozusagen zweimal im Tierheim? Einmal als Attribut und einmal in der Liste. Was würde es bedeuten wenn er nur als Attribut aber nicht in der Liste gespeichert wäre?
Grunddatentypen haben nichts in Namen verloren. `liste_tiere` sollte einfach nur `tiere` heissen.
Bei dem Namen `get_Tier()` erwartet der Leser das diese Methode etwas zurückliefert, und so überhaupt nicht das sie das gesamte Tierheim per `print()` ausgibt. Die sollte also beispielsweise `print()` heissen.
``for i in range(len(sequence)):`` nur um dann mit `i` auf die Elemente in `sequence` zuzugreifen ist in Python ein „anti pattern“. Man kann direkt, ohne Umweg über einen Indexzugriff, über die Elemente von Seqzenztypen iterieren.
In der Methode wird auch plötzlich `anzahl_Tiere` an das `Tierheim`-Objekt gebunden. Das gehört da aber gar nicht hin. Das wäre vielleicht ein lokaler Name, wenn es denn nicht gleich darauf nur ein einziges mal in einem `print()` verwendet werden würde. Attribute sollten alle nach abarbeiten der `__init__()`-Methode vorhanden sein, und nicht nachträglich in Methodenaufrufen entstehen.
Der Klassenname beschreibt *ein* Exemplar. Alles was von `Tier` abgeleitet ist, ist also falsch benannt, weil diese Klassen jeweils *ein* Tier beschreiben, also im Singular benannt werden müssen.
Da es nur ein `Tierheim` gibt, macht der Zusatz `_a` in `tierheim_a` keinen Sinn.
Zwischenstand:
Code: Alles auswählen
#!/usr/bin/env python3
class Tier:
def __init__(self, art, name, einlieferungsdatum, id_):
self.art = art
self.name = name
self.einlieferungsdatum = einlieferungsdatum
self.id = id_
class Tierheim:
def __init__(self, name):
self.name = name
self._tiere = []
def add(self, tier):
self._tiere.append(tier)
def print(self):
for tier in self._tiere:
print(tier.name)
print(len(self._tiere))
class Saeugetier(Tier):
def __init__(self, saeugerklasse, art, name, einlieferungsdatum, id_):
super().__init__(art, name, einlieferungsdatum, id_)
self.saeugerklasse = saeugerklasse
class Vogel(Tier):
def __init__(
self,
ist_flugfaehig,
schnabelfarbe,
art,
name,
einlieferungsdatum,
id_,
):
super().__init__(art, name, einlieferungsdatum, id_)
self.ist_flugfaehig = ist_flugfaehig
self.schnabelfarbe = schnabelfarbe
class Fisch(Tier):
def __init__(
self, lebensraum, fischart, art, name, einlieferungsdatum, id_
):
super().__init__(art, name, einlieferungsdatum, id_)
self.lebensraum = lebensraum
self.fischart = fischart
def main():
affe = Saeugetier(
"Pflanzentiere", "Säugetier", "Koko", "10.12.2014", 18874
)
fisch = Fisch(
"Süßwasser", "Raubfisch", "Fisch", "Nemo", "20.05.2010", 17896
)
vogel = Vogel(True, "blau", "Vogel", "Sky", "02.12.2018", 147896)
tierheim = Tierheim("AAAA")
tierheim.add(affe)
tierheim.add(fisch)
tierheim.add(vogel)
tierheim.print()
if __name__ == "__main__":
main()
Da das Python ist, könnte man aus den `Tierheim`-Objekten etwas machen das eine Länge hat und iterierbar ist. Was die `print()`-Methode noch ein bisschen schlanker werden lässt.
Die `Tier.art` scheint mir Redundant zu sein, weil die ja fest mit der Klasse verbunden zu sein scheint. Im einfachsten Fall kann man da einfach den Klassennamen für hernehmen, oder eine Konstante die auf den `Tier`-Klassen definiert ist, wenn der Tierartname vom Klassennamen abweichen können soll.
Code: Alles auswählen
#!/usr/bin/env python3
class Tier:
def __init__(self, name, einlieferungsdatum, id_):
self.name = name
self.einlieferungsdatum = einlieferungsdatum
self.id = id_
@property
def art(self):
return self.__class__.__name__
class Tierheim:
def __init__(self, name):
self.name = name
self._tiere = []
def __len__(self):
return len(self._tiere)
def __iter__(self):
return iter(self._tiere)
def add(self, tier):
self._tiere.append(tier)
def print(self):
for tier in self:
print(f"{tier.name} ({tier.art})")
print(len(self))
class Saeugetier(Tier):
def __init__(self, saeugerklasse, name, einlieferungsdatum, id_):
super().__init__(name, einlieferungsdatum, id_)
self.saeugerklasse = saeugerklasse
class Vogel(Tier):
def __init__(
self, ist_flugfaehig, schnabelfarbe, name, einlieferungsdatum, id_
):
super().__init__(name, einlieferungsdatum, id_)
self.ist_flugfaehig = ist_flugfaehig
self.schnabelfarbe = schnabelfarbe
class Fisch(Tier):
def __init__(self, lebensraum, fischart, name, einlieferungsdatum, id_):
super().__init__(name, einlieferungsdatum, id_)
self.lebensraum = lebensraum
self.fischart = fischart
def main():
affe = Saeugetier("Pflanzentiere", "Koko", "10.12.2014", 18874)
fisch = Fisch("Süßwasser", "Raubfisch", "Nemo", "20.05.2010", 17896)
vogel = Vogel(True, "blau", "Sky", "02.12.2018", 147896)
tierheim = Tierheim("AAAA")
tierheim.add(affe)
tierheim.add(fisch)
tierheim.add(vogel)
tierheim.print()
if __name__ == "__main__":
main()