Seite 1 von 1

Klassen Attribute und Methoden

Verfasst: Freitag 1. Mai 2020, 13:48
von lalelu169
Hallo zusammen,
ich habe angefangen mich mit Klassen zu beschäftigen und habe eine Frage zu Attributen und Methoden.

Bisher mache ich es so, dass ich einen Teil der Attribute über den Klassenaufruf übergebe (attribut 1 und attribut2), sowie einfache Berechnungen von neuen Attributen (insbesondere solche die sich im Programmablauf nicht ändern) ebenfalss in __init__ erzeuge.

In den Methoden werden bei mir ebenfalls Attribute erzeugt (z.B. attribut4). Dies sind üblicherweise Attribute, die sich im Programmverlauf auch ändern. Teilweise muss ich diese Attrbute dann allerdings in den Methoden neu erstellen und mit einem Wert vorbelegen (z.B. self.zaehler = 0, self.liste = []). Sollte man diese nicht auch in __init__m zuerst initialisieren. Nachteil wäre, dass __init__ dann ziemlich aufgebläht wäre.

Gibt es generelle Kommentare zu meiner Vorgehensweise.
Vielen Dank und Gruesse


Code: Alles auswählen

class Klasse1():
    def __init__(self, v1, v2):
        self.attribut1 = v1
        self.attribut2 = v2
        self.attribut3 = self.attribut1 * self.attribut2
        
    def m1(self):
        """macht irgendetwas und erzeugt ein neues Attribut der Klasse1"""
        self.attribut4 = [(self.attribut1 + i*i) for i in range(self.attribut2)]
      
    def print_attribute(self):
        for attr in self.__dict__:
            print(attr, getattr(self, attr))

k = Klasse1(10,20)
k.m1()
k.print_attribute()

Re: Klassen Attribute und Methoden

Verfasst: Freitag 1. Mai 2020, 13:51
von __deets__
Man macht das im __init__. Denn sonst musst du ueberall defensiv gegen nicht-vorhandene Attribute programmieren, und auch dem Leser des Codes erschliesst sich nicht, welche Attribute die Klasse eigentlich alle hat. Und Leser bist auch du selbst, in 2 Wochen, wenn du probierst zu verstehen, was du da geschaffen hast.

Ich weiche von dieser Regel nur sehr selten ab, und dann nur in der Form, dass ich den Konstruktor (oder Initialisator) aufteile in mehrerer Methoden, die aber *immer* alle aufgerufen werden. Da geht's dann um einen Kompromiss zwischen zwei Arten der Lesbarkeit.

Re: Klassen Attribute und Methoden

Verfasst: Freitag 1. Mai 2020, 14:32
von __blackjack__
lalelu169: Für einfache berechnete Attribute wie `attribut3` in Deinem Beispiel nimmt man meistens Properties, damit keine Inkonsistenzen entstehen können.

Und es sollten auch nicht viele Attribute werden. Da gilt das gleiche wie für lokale Variablen, man kann nur begrenzt viele Dinge im Kopf behalten. Irgendwas zwischen 5 und 10, wobei ich 10 schon ziemlich viel finde.

Weil das alles so generisch ist in dem Beispiel: Deine Klassen repräsentieren auch irgend etwas und sind nicht nur Funktionen mit vielen globalen Variablen in eine Klasse verschoben‽

Bei `print_attribute()` hätte ich fast angemerkt, dass das falsch benannt ist, weil es ja nicht nur *in* Attribut ausgibt, wobei ich jetzt nicht so genau weiss ob Du `attribute` hier Englisch oder Deutsch gemeint hast. Ein Beispiel warum ”Denglisch” nicht so gut ist.

Auf `__dict__` sollte man nicht direkt zugreifen. Erstens muss das Attribut nicht existieren und auch wenn es existiert müssen da nicht alle Attribute enthalten sein. Die `vars()`-Funktion deckt auch diese Fälle ab. Selbst wenn man über `__dict__` geht, ist das `getattr()` unnötig umständlich, denn man kann von einem Wörterbuch über `items()` direkt Schlüssel und Werte zum iterieren abfragen. Properties sind aber weder in `__dict__` noch in `vars()` enthalten.

Der Docstring der Methode ist ein wenig ungenau, denn auf Klassen kann man ja auch Attribute erzeugen.

Code: Alles auswählen

#!/usr/bin/env python3


class Klasse:
    def __init__(self, wert_a, wert_b):
        self.wert_a = wert_a
        self.wert_b = wert_b
        #
        # Oder eine leere Liste wenn das sinnvoll wäre für Methoden die dieses
        # Attribut verwenden.
        #
        self.werte = None

    @property
    def wert_c(self):
        return self.wert_a * self.wert_b

    def methode(self):
        """
        Macht irgendetwas und erzeugt kein neues Attribut auf dem Objekt.
        """
        self.werte = [self.wert_a + i ** 2 for i in range(self.wert_b)]

    def attribute_ausgeben(self):
        for name, wert in vars(self).items():
            print(name, wert)
        print("wert_c", self.wert_c)


def main():
    klasse = Klasse(10, 20)
    klasse.methode()
    klasse.attribute_ausgeben()


if __name__ == "__main__":
    main()

Re: Klassen Attribute und Methoden

Verfasst: Freitag 1. Mai 2020, 16:35
von lalelu169
Vielen Dank für die Hinweise

@__blackjack__
mit properties kann ich leider noch nichts anfangen. Schau ich mir noch an...
In der Tat ist das Beispiel recht generisch. Ein Teil der Attribute in meinem Projekt sind sicherlich globale Variablen, die ich nun beim Erstellen eines Klassenobjektes übergebe, weil ich gelernt habe, dass globale Variablen nicht verwendet werden sollen. Ein anderer Teil der Attribute wird allerdings von den Methoden in der Klasse generiert und auch im Programmablauf regelmässig geändert. Insgesamt habe ich drei Klassen mit jeweils 5-10 Attributen und 3-6 Methoden. Ist also noch ganz gut machbar.

Das print_attribute() habe ich aus einem Lehrbuch und fand es ganz praktisch beim debuggen, da damit alle Attribute aus der Klasse ausgegeben werden. Ich nutze es nur dafür.

Re: Klassen Attribute und Methoden

Verfasst: Freitag 1. Mai 2020, 16:58
von __blackjack__
@lalelu169: Es sind hoffentlich keine globalen Variablen als Attribute verkleidet, denn es ist ja nicht sinnvoll die globalen Variablen einfach nur anders zu schreiben damit sie formal keine globalen Variablen mehr sind, aber semantisch doch noch. Die Frage ist ob das sinnvolle Klassen sind, die ein Ding oder Konzept beschreiben und alle Attribute zusammen den Zustand dieses Dings oder Konzeptes modellieren.

Die `print_attribute()` ist überflüssig weil man das auch mit ``print(vars(object))`` von aussen erreichen kann, beziehungsweise ist das halt keine sinnvolle Methode wenn man die auf jeder Klasse definieren muss, wo man sie doch einfach als Funktion schreiben könnte und dann mit *jedem* Objekt verwenden kann. Aber wie gesagt ist `print()` und `vars()` oder `pprint.pprint()` statt `print()` ausreichend. Oder man verwendet das `attrs`-Package, dann kann man einfach die Objekte selbst ausgeben.

Code: Alles auswählen

#!/usr/bin/env python3
from attr import attrib, attrs
from prettyprinter import cpprint, install_extras

install_extras(["attrs"])


@attrs
class Klasse:
    wert_a = attrib()
    wert_b = attrib()
    #
    # Oder eine „factory“ mit `list` falls das sinnvoll wäre für Methoden die
    # dieses Attribut verwenden.
    #
    werte = attrib(default=None)

    @property
    def wert_c(self):
        return self.wert_a * self.wert_b

    def methode(self):
        """
        Macht irgendetwas und erzeugt kein neues Attribut auf dem Objekt.
        """
        self.werte = [self.wert_a + i ** 2 for i in range(self.wert_b)]


def main():
    klasse = Klasse(10, 20)
    klasse.methode()
    cpprint(klasse)


if __name__ == "__main__":
    main()
Ausgabe:

Code: Alles auswählen

Klasse(
    wert_a=10,
    wert_b=20,
    werte=[
        10,
        11,
        14,
        19,
        26,
        35,
        46,
        59,
        74,
        91,
        110,
        131,
        154,
        179,
        206,
        235,
        266,
        299,
        334,
        371
    ]
)
Ohne das `prettyprinter`-Modul, nur mit `print()` sähe die Ausgabe immerhin noch so aus:

Code: Alles auswählen

Klasse(wert_a=10, wert_b=20, werte=[10, 11, 14, 19, 26, 35, 46, 59, 74, 91, 110, 131, 154, 179, 206, 235, 266, 299, 334, 371])