Klassen, Methoden, Funktionen

Code-Stücke können hier veröffentlicht werden.
Antworten
HerrAyas
User
Beiträge: 5
Registriert: Montag 1. April 2024, 14:51

Hi zusammen,
neu hier - falls ich das falsche Board gewählt habe, sorry.
Wie bekomme ich beim kopieren aus VStudio die Zeilennummern mit?
Egal, ich hab's fett gedruckt.
Klasse definiert, Methode "Vektoraddition" implementiert und auf einem neuen Objekt p3 aufgerufen.
In der Methode dachte ich, dass ich zuerst ein Objekt "self" erzeugen muss, dann war das Resultat aber immer 99/99 (init-werte)
Mit # funktioniert es tadellos. Aber warum? Selbst wenn ich das "self" neu erzeuge, wird doch gleich danach self.x und self.y überschrieben...
Der zweite fette Eintrag betrifft einen Kommentar. Habe ich das richtig verstanden?
Danke und lg,
HrAyas


class Punkt():
def __init__(self, x = 99, y = 99): #IMMER den Attributen Standardwerte zuordnen, sonst gibts Fehler!
self.x = x
self.y = y

def __str__(self):
return f'( {self.x} | {self.y} )'

def add(self, p1, p2):
#self = Punkt() #wenn diese Zeile drin bleibt, kommt immer ein "jungfräulicher" Punkt raus! WARUM?
self.x = p1.x + p2.x
self.y = p1.y + p2.y
return(self)

p1 = Punkt() #ohne __init Anfangswerte gibt es hier eine Fehlermeldung, weil nur deklariert, aber nicht initialisiert wurde (?stimmt das?)
p1.x = 17 #lässt man init ganz weg, fällt niemandem auf, dass bei Punkt() was fehlt
p1.y = 12

p2 = Punkt(-9,-4)

p3 = Punkt()
p3.add(p1,p2)
print(f"Punkt 1: {p1}")
print(f"Punkt 2: {p2}")
print(f"Punkt 3: {p3}")
Benutzeravatar
__blackjack__
User
Beiträge: 13456
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@HerrAyas: Einrückung ist wichtig in Python, darum bitte Quelltext im Beitrag immer in einen Code-Block setzten, damit die Einrückung in der Anzeige erhalten bleibt.

Man sollte Attributen nicht immer Standardwerte zuordnen sondern nur wenn das sinnvolle Werte sind. 99 ist das eher nicht. 0 wäre es vielleicht wenn man bei einem Punkt ohne explizite Werte gerne den Nullpunkt haben möchte.

Wenn man Werte für `x` und `y` haben möchte macht es eher keinen Sinn erst einem Punkt mit den Defaultwerten zu erstellen und dann gleich danach die tatsächlichen Werte zu setzen. Dafür ist die `__init__()` doch da, dass man das nicht so umständlich machen muss.

Wenn man `__init__()` ganz weg lässt (und auch keine sinnvolle `__init__()` von der Basisklasse geerbt wird), dann macht man etwas falsch. Nach dem erstellen eines Objektes sollte das in einem benutzbaren Zustand sein, und vor allem alle Attribute besitzen. Das fällt sonst auch auf wenn man versucht auf Attribute zuzugreifen die es (noch) gar nicht gibt.

Die `add()`-Methode wäre mit der auskommentierten Zeile falsch, weil dann das übergebene `self` gar nicht benutzt würde und das damit keine Methode wäre.

So wie es jetzt da steht ist das schräg weil da bei einer Addition *drei* Punkt-Objekte beteiligt sind, statt wie man erwarten würde nur zwei. So ein Punktobjekt wäre sowieso besser ein Wertobjekt, also eines das nicht verändert wird. Die Addition würde dann ein *neues* Punktobjekt als Ergebnis liefern.

Per Konvention liefern Methoden die das Objekt auf dem sie aufgerufen wurden *verändern* *nicht* als Ergebnis zurück. Also ein ``return self`` ist etwas wo man mal kurz innehalten sollte und überlegen was man da gerade macht.

Eine Zuweisung an `self` macht keinen Sinn, weil das *nicht* das Objekt verändert das vorher an diesen Namen gebunden war.

Üblicherweise würde man hier `__add__()` implementieren um ``+`` verwenden zu können.

Code: Alles auswählen

#!/usr/bin/env python3


class Punkt:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __str__(self):
        return f"( {self.x} | {self.y} )"

    def __add__(self, other):
        return Punkt(self.x + other.x, self.y + other.y)


def main():
    p1 = Punkt(17, 12)
    p2 = Punkt(-9, -4)
    p3 = p1 + p2
    print(f"Punkt 1: {p1}")
    print(f"Punkt 2: {p2}")
    print(f"Punkt 3: {p3}")


if __name__ == "__main__":
    main()

Code: Alles auswählen

- (void)countSheep {
    unsigned int sheep = 0;
    while ( ! [self isAsleep]) { ++sheep; }
}
HerrAyas
User
Beiträge: 5
Registriert: Montag 1. April 2024, 14:51

Code: Alles auswählen

def add(self, p1, p2):
        #self = Punkt()             #WHY? WITH this line, every result is 99/99 
        self.x = p1.x + p2.x
        self.y = p1.y + p2.y
        return(self)
    # Now it's getting creepy: __add__ is called by a "+" !!!

    def __add__(self, other):
        return Punkt(self.x + other.x, self.y + other.y)
    
    def __mul__(self,other):
        return Punkt(self.x*other.x, self.y*other.y)
Ahh, vielen Dank für die sehr ausführliche Antwort. Jetzt habe ich auch die code-Blöcke verstanden... 99 nur, um "default" zu verdeutlichen, könnte ja sonst aus versehen auch "0" rauskommen.
p1 und p2 habe ich unterschiedlich erstellt, weil ich rumspielen wollte, p2 ist natürlich effizienter.
IMMER __init__ Methode bei JEDER Klasse implementieren, habe ich das richtig verstanden?

Das mit dem return self und drei eingaben bei nur 2 zu addierenden Punkten ist wild gewachsen. Die ersten Versuche haben immer gesagt "nur 2 Inputs, wo drei verlangt werden"

Dein Vorschlag des "operator overload" kam genau eine Folie später :) habe es auch mit "self" und "other" gemacht...

Eine Frage habe ich noch zu Deinem Hinweis, bzw. zur korrekten Beschreibung
Per Konvention liefern Methoden die das Objekt auf dem sie aufgerufen wurden *verändern* *nicht* als Ergebnis zurück. Also ein ``return self`` ist etwas wo man mal kurz innehalten sollte und überlegen was man da gerade macht.
Wenn ich die Zeilen

Code: Alles auswählen

p3 = Punkt()
p3 = p3.add(p1,p2)
anschaue: die 1. erzeugt ein Objekt namens p3 der Klasse Punkt. Korrekt? Hat jetzt die Werte p3.x =99 =p3.y Korrekt?
Die 2. Zeile "überschreibt" das Objekt p3 mit einem neuen Objekt, das aus p3, p1 und p2 mit Hilfe der Methode "add" gewonnen wird. Korrekt? Sagt man " ich habe die Methode 'add' auf p3 aufgerufen"? Und verändere damit p3?
Die Konvention verbietet mir das durch die Methode veränderte Objekt zurückzugeben? Wie komme ich dann sonst an das Ergebnis?
Ja, __add__ ist viel eleganter - nur wie würde eine "der Konvention gehorchende" Methode aussehen? Ohne 3 Parameter? Ohne "return self"?

Herzlichen Dank für eure Geduld.. alles nicht so einfach, wenn man von anderen Sprachen kommt :)
HrAyas
Benutzeravatar
__blackjack__
User
Beiträge: 13456
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@HerrAyas: Eine sinnvolle Klasse braucht eine `__init__()`-Methode. Man muss die nicht immer selber implementieren wenn die Basisklasse schon eine passende `__init__()` mitbringt oder die beispielsweise dynamisch von Code mit einem Klassendekorator ”geschrieben” wird.

In der zweiten Zeile wird eben das Objekt nicht überschrieben, das ist das Objekt das vorher schon an `p3` gebunden war. Deswegen macht das ja auch keinen Sinn das Objekt selbst zurückzugeben. An das Objekt kommt man auch so, denn man hat das ja bereits. Sonst könnte man die Methoden nicht darauf aufrufen.

Code: Alles auswählen

- (void)countSheep {
    unsigned int sheep = 0;
    while ( ! [self isAsleep]) { ++sheep; }
}
HerrAyas
User
Beiträge: 5
Registriert: Montag 1. April 2024, 14:51

danke!
Antworten