Attribute Error trotz "Erbung" der Klasse

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
ImNotFresh
User
Beiträge: 1
Registriert: Dienstag 12. Dezember 2023, 16:48

Hey Leute,

ich bin recht neu in Python und mache das grade in einem Unimodul.
Ich habe folgenden Code geschrieben und bekomme beim Nutzen der "p_on_line" Funktion immer einen AttributeError?
Obwohl ich die LineSeg Klasse der Point Klasse untergeordnet habe.


Also Eingabe in die Konsole nutze ich: C.LineSeg.p_on_line(A,B)
Und die Konkrete Fehlermeldung ist: AttributeError: 'Point' object has no attribute 'p_on_line'
kann jemand helfen?
LG Leo

Code: Alles auswählen

import math

class Point:
    
    #---------Punkte erschaffen, verschieben und überlappung checken
    
    counter = 0
    
    def __init__ (self, xWert, yWert) :
        self.xWert = xWert
        self.yWert = yWert
        type(self).counter += 1
    
    def __del__(self):
        type(self).counter -= 1

    def move(self, dx:float, dy:float):
        self.xWert += dx
        self.yWert += dy

    def coincide(self, punkt):
        if self.xWert == punkt.xWert and self.yWert == punkt.yWert :
            return True 
        return False

class Circle(Point):
    
    #----Kreise Erschaffen und checken, ob Punkt Teil davon ist
    
    def __init__ (self, xWert, yWert, radius):
        super().__init__(xWert, yWert)
        self.radius = radius
    
    def coincide(self, punkt):
        if math.sqrt(punkt.xWert**2 + punkt.yWert**2) <= self.radius :
            return True 
        return False    

class LineSeg(Point):
    
    #schauen ob Punkt auf einer Strecke zwischen zwei anderen Punkten liegt
    
    def p_on_line(self, punkt1, punkt2):
        if (self.yWert-punkt1.xWert)*(punkt2.xWert - punkt1.xWert) == (punkt2.yWert-punkt1.yWert)*(self.xWert - punkt1.xWert):
            if (min(punkt1.xWert, punkt2.xWert) <= self.x <= max(punkt1.xWert, punkt2.xWert)) and \
               (min(punkt1.yWert, punkt2.yWert) <= self.y <= max(punkt1.yWert, punkt2.yWert)):  
                return True
        return False
[/code ]
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist wirklich ziemlich schlimm was die Vererbung angeht. Weder ein Kreis noch eine Linie sind Punkte. Diese Vererbungschierchie sollte also entfernt werden.

Zu deinem Problem; Das ist nicht nachvollziebar, weil du nicht zeigst, wie C.LineSeg zustande kommt. Notduerftig repariert funktioniert das:

Code: Alles auswählen

import math

class Point:

    #---------Punkte erschaffen, verschieben und überlappung checken

    counter = 0

    def __init__ (self, xWert, yWert) :
        self.xWert = xWert
        self.yWert = yWert
        type(self).counter += 1

    def __del__(self):
        type(self).counter -= 1

    def move(self, dx:float, dy:float):
        self.xWert += dx
        self.yWert += dy

    def coincide(self, punkt):
        if self.xWert == punkt.xWert and self.yWert == punkt.yWert :
            return True
        return False

class Circle(Point):

    #----Kreise Erschaffen und checken, ob Punkt Teil davon ist

    def __init__ (self, xWert, yWert, radius):
        super().__init__(xWert, yWert)
        self.radius = radius

    def coincide(self, punkt):
        if math.sqrt(punkt.xWert**2 + punkt.yWert**2) <= self.radius :
            return True
        return False

class LineSeg(Point):

    #schauen ob Punkt auf einer Strecke zwischen zwei anderen Punkten liegt

    def p_on_line(self, punkt1, punkt2):
        if (self.yWert-punkt1.xWert)*(punkt2.xWert - punkt1.xWert) == (punkt2.yWert-punkt1.yWert)*(self.xWert - punkt1.xWert):
            if (min(punkt1.xWert, punkt2.xWert) <= self.x <= max(punkt1.xWert, punkt2.xWert)) and \
               (min(punkt1.yWert, punkt2.yWert) <= self.y <= max(punkt1.yWert, punkt2.yWert)):
                return True
        return False

l = LineSeg(100, 200)
print(l.p_on_line(Point(0, 1), Point(2, 3)))
Benutzeravatar
__blackjack__
User
Beiträge: 13121
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@ImNotFresh: Ich möchte das auch noch mal unterstreichen das die Vererbung so falsch ist. Vererbung ist eine „Ist-ein(e)“-Beziehung. Abgeleitete Klassen sind auch gleichzeitig das was die Basisklasse beschreibt. Ein Kreis *ist* kein Punkt, der *hat* einen (Mittel)Punkt. Das ist keine Vererbung, das ist Komposition.

Dann bitte den `counter` und `__del__()` aus dem Code nehmen. Das funktioniert so nicht und kann unter Umständen sogar aktiv schädlich sein. Es ist nicht garantiert *wann* `__del__()` aufgerufen wird, nicht einmal *ob* es jemals aufgerufen wird. Und alleine die Existenz einer `__del__()`-Methode kann unter bestimmten schon verhindern, dass die betreffenden Objekte jemals wieder freigegeben werden (in dem Fall wird dann auch die Methode nie aufgerufen).

Variablen haben auf Klassen auch grundsätzlich eher nichts zu suchen, denn das ist globaler Zustand. Den will man nicht haben.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Das `Wert` bei `xWert` und `yWert` kann man sich sparen. `x` und `y` für Koordinaten sind eine der wenigen Ausnahmen wo Einbuchstabige Namen tatsächlich mal okay sind. Neben `i`, `j`, und `k` für Indexe und gannzahlige Laufvariablen in Schleifen.

Man sollte auch nicht natürliche Sprachen mischen. Entweder `Point` und `x_value` oder `Punkt` und `x_wert`, aber kein Denglisch. Ich würde Englisch empfehlen.

Kryptische Abkürzungen in Namen sollte man vermeiden. Wenn man `LineSegment` meint, sollte man nicht nur `LineSeg` schreiben, oder gar nur `p_*` wenn man `point_*` meint.

Wenn der Rückgabewert, oder generell ein Wert, abhängig von einer Bedingung `True` oder `False` sein soll, dann braucht man kein ``if``, denn das Ergebnis von der Bedingung ist ja bereits der gewünschte Wert. Oder zumindest dessen Gegenteil — was man mit ``not`` negieren kann.

Bei der Testmethode an der Du hängst, ist das ja total verquer. Da wird das Liniensegment behandelt als wäre es ein Punkt und der Test bekommt zwei Punkte als Argumente. Dabei ist das doch genau umgekehrt: ein Liniensegment *hat* bereits zwei Endpunkte und müsste für diesen Text *einen* Punkt als Argument bekommen.

Und auch die Klassen nach dem Punkt könnten recht trivial eine `move()`-Methode verpasst bekommen.

Zwischenstand (ungetestet):

Code: Alles auswählen

import math


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

    def move(self, x_offset, y_offset):
        self.x += x_offset
        self.y += y_offset

    def coincides(self, other):
        return self.x == other.x and self.x == other.x


class Circle:
    def __init__(self, center, radius):
        self.center = center
        self.radius = radius

    def move(self, x_offset, y_offset):
        self.center.move(x_offset, y_offset)

    def coincides(self, point):
        return math.sqrt(point.x**2 + point.y**2) <= self.radius


class LineSegment:
    def __init__(self, endpoint_a, endpoint_b):
        self.endpoint_a = endpoint_a
        self.endpoint_b = endpoint_b

    def move(self, x_offset, y_offset):
        self.endpoint_a.move(x_offset, y_offset)
        self.endpoint_b.move(x_offset, y_offset)

    def is_point_on_line(self, point):
        return (
            (
                (point.y - self.endpoint_a.x)
                * (self.endpoint_b.x - self.endpoint_a.x)
                == (self.endpoint_b.y - self.endpoint_a.y)
                * (point.x - self.endpoint_a.x)
            )
            and (
                min(self.endpoint_a.x, self.endpoint_b.x)
                <= point.x
                <= max(self.endpoint_a.x, self.endpoint_b.x)
            )
            and (
                min(self.endpoint_a.y, self.endpoint_b.y)
                <= point.y
                <= max(self.endpoint_a.y, self.endpoint_b.y)
            )
        )
`Circle.coincides()` ist falsch, weil das den Kreismittelpunkt in keiner Weise berücksichtigt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6744
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

__blackjack__ hat geschrieben: Dienstag 12. Dezember 2023, 18:58 Zwischenstand (ungetestet):

Code: Alles auswählen

class Point:
# ...
    def coincides(self, other):
        return self.x == other.x and self.x == other.x
Hier prüfst du doppelt auf die x-Werte, aber "vergisst" die y-Werte.

Das müsste insofern sicherlich ``self.x == other.x and self.y == other.y`` heißen.

Und das Verschieben könnte man über die Einführung einer Vector-Klasse implementieren, dann sieht es IMHO etwas sauberer aus. :)
Benutzeravatar
__blackjack__
User
Beiträge: 13121
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@snafu: Ähm, ja, äh, das mit dem `x` statt `y` hatte ich *natüüürlich* als Test eingebaut, um zu schauen ob auch alle aufpassen. 😳

(Gut das ich da immer „ungetestet“ dran schreibe wenn es ungetestet ist. 😉)

Ist so ein bisschen die Frage wozu das am Ende gut sein soll, aber ich versuche meistens erst einmal von Werttypen auszugehen und hätte da erst einmal nichts implementiert was den Punkt tatsächlich verschiebt, sondern eher ``+`` überladen, so dass man jeweils ein *neues* verschobenes Objekt bekommt. Dann hätte man Punkte einfach addieren können.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten