Seite 1 von 1

Klassen: Einfache Baumstruktur, Pointer

Verfasst: Montag 18. August 2014, 18:08
von losgehts
Hallo,

ich bin Neuling in Sachen Python (auch wenn ich mich hier schon vor Jahren angemeldet habe). Sollte ich irgendwie gegen Regeln verstoßen, so ist das ungewollt. Macht mich einfach darauf aufmerksam.

Ich benutze Python 3.4

Ich bin dabei, eine Baumstruktur für mein Projekt zu schreiben. Doch ich habe Schwierigkeiten. Meinen Code habe ich versucht, auf das Problem zu entschlacken:

Code: Alles auswählen

class msgTreeChild:
    __id = ""
    __val = ""
    __parent = None
    __childs = []
    def __init__(self, ID, val):
        self.__val = val
        self.__id = ID

    def addChild(self, ID, Val):
        newChild = msgTreeChild(ID, Val)
        self.__childs.append(newChild)
        return newChild
  
    def getChildList(self):
        return self.__childs


if __name__ == "__main__":
    x = msgTreeChild(1, 2)
    a = x.addChild(2, 3)
    b = x.addChild(3, 1)
    c = x.addChild(4, 2)
    d = x.addChild(5, 2.5)

    # meine Erwartung: x: 4 Kinder,   a: 0 Kinder,   b: 0 Kinder,   c:0,   d:0
    print("Anzahl der Kinder von a: " + str(len(a.getChildList())))
    print(a.getChildList() == b.getChildList())
    print(a.getChildList() is b.getChildList())
Ich bin Neuling, daher bin ich froh, wenn ihr mich verbessert. Ich schreibe kurz, was ich erwartet habe (Schade, wie bekomme ich diese Zeilennummern in die Code-Tags, das wär praktisch):

x = msgTreeChild(1, 2) Es wird eine Instanz der Klasse kreiert und die Methode __init__ wird aufgerufen. Sie weißt der Instanz die Inhalte der Variablen zu.

a = x.addChild(2, 3) Es wird die Methode addChild aufgerufen. Es entspricht quasi:
addChild(self = x, ID = 2, Val = 3).

newChild = msgTreeChild(ID, Val) Es wird eine neue Instanz der Klasse kreiert (damit existieren zwei Instanzen). Die __init__-Methode wird aufgerufen, diese weist die Werte 2,3 den Variablen __id, __val zu.
Der Variablen newChild wird diese neue neue Klasse zugewiesen.

self.__childs.append(newChild) self entspricht x. Es wird also der Liste __childs der ersten Klasse die neue Klasse hinzugefügt.

return newChild Diese neue Klasse wird der Variablen a zugewiesen.

Meinem Verständnis nach müsste ich nun zwei Klassen haben (x und a). x hat in seiner liste __childs einen Eintrag und a hat in seiner Liste __childs kein Element (leere Liste).

Wie ich nun an der Ausgabe der print()-Befehle erkenne, verhält es sich anders, als ich mir das vorstelle.
Kann mir jemand aufzeigen, wo ich den Denkfehler habe? Vielen Dank im Voraus!

Wenn ich a.getChildList() is b.getChildList() == True richtig interpretiere, dann bedeutet das, dass es nur eine Liste "__childs" gibt, und a.__childs sowie b.__childs auf die selbe Instanz verweisen?

Ich bin Euch wirklich dankbar, wenn ihr mir aufzeigt, wo ich das Brett vorm Kopf habe.

Grüße, Ulrich

PS: das mit den Zeilennummern beim Code im Forum habe ich noch rausbekommen: einfach die Code-Tags mit großem "C" beginnen, statt mit kleinem ;-).

Re: Klassen: Einfache Baumstruktur, Pointer

Verfasst: Montag 18. August 2014, 18:31
von Sirius3
@losgehts: Klassenattribute sind kein Ersatz für Instanzattribute. Diese werden nur einmal, beim Definieren der Klasse, erzeugt; damit hast Du nur eine __childs-Liste, die zwischen allen Instanzen der Klasse geteilt wird.

Re: Klassen: Einfache Baumstruktur, Pointer

Verfasst: Montag 18. August 2014, 18:47
von losgehts
Hallo Sirius3,

vielen Dank! Oha, das Wort "Instanzattribut" kenne ich bis jetzt noch nicht.

Da muss ich wohl noch einmal ordentlich recherchieren und mich bilden.

Ich verstehe nicht, weshalb die variablen x, a, b, c sich zwar die Liste __childs teilen, jedoch die Werte __val und __id nicht.
Spendiere ich der Klasse noch die Funktionen

Code: Alles auswählen

    def getID(self):
        return self.__id
    
    def getVal(self):
        return self.__val
so ist
a.getVal() == 3
b.getVal() == 1
und x.getVal() == 2
Wo ist da der Unterschied?

Vielen Dank!
Grüße, Ulrich

Re: Klassen: Einfache Baumstruktur, Pointer

Verfasst: Montag 18. August 2014, 19:01
von Sirius3
@losgehts: Du setzt ja __val und __id in der __init__-Methode. Damit überschreibst Du die Klassenattribute durch Instanzattribute, so dass die Klassenattribute verdeckt sind.
Übrigens solltest Du die beiden Unterstriche vor den Attributen weglassen, dann brauchst Du auch nicht für jedes Attribut eine getter-Methode schreiben, sondern kannst direkt per x.id darauf zugreifen.

Re: Klassen: Einfache Baumstruktur, Pointer

Verfasst: Montag 18. August 2014, 19:12
von BlackJack
Und ergänzend dazu entspricht die Namensgebung auch nicht dem Style Guide for Python Code. Mindestens der Klassenname ist in der Schreibweise sehr ungünstig.

Edit: Die Mehrzahl von „child” ist „children”. Und man sollte Namen nicht ohne Not abkürzen.

Die `addChild()`-Methode macht mehr als man bei dem Namen erwarten würde, nämlich das man ihr einen Kindknoten übergibt und sie den hinzufügt — und nichts zurück gibt.

Code: Alles auswählen

class Tree:

    def __init__(self, id_, value, parent=None):
        self.id = id_
        self.value = value
        self.parent = parent
        self.children = list()
 
    def create_child(self, id_, value):
        result = Tree(id_, value, self)
        self.children.append(result)
        return result
  
 
def main():
    tree = Tree(1, 2)
    a = tree.create_child(2, 3)
    b = tree.create_child(3, 1)
    c = tree.create_child(4, 2)
    d = tree.create_child(5, 2.5)
 
    # meine Erwartung: tree: 4 Kinder, a: 0 Kinder, b: 0 Kinder, c: 0, d: 0
    print('Anzahl der Kinder von a: ', len(a.children))
    print(a.children == b.children)
    print(a.children is b.children)


if __name__ == '__main__':
    main()

Re: Klassen: Einfache Baumstruktur, Pointer

Verfasst: Montag 18. August 2014, 19:41
von Hyperion
BlackJack hat geschrieben: Die `addChild()`-Methode macht mehr als man bei dem Namen erwarten würde, nämlich das man ihr einen Kindknoten übergibt und sie den hinzufügt — und nichts zurück gibt.
Und weil die Trennung von Befehl und Abfrage so wichtig ist, gibt es für dieses Konzept sogar einen eigenen Namen: Command Query Separation :-)

Re: Klassen: Einfache Baumstruktur, Pointer

Verfasst: Montag 18. August 2014, 20:28
von losgehts
Hallo,

@Sirius3, vielen Dank für die Ausführung. Ich habe das jetzt ein wenig ausprobiert und verstanden. Muss aber noch einmal richtig in diesem Thema nachlesen (Namensräume, Klassen, wo sind welche Variablen wann sichtbar, wie wird richtig initialisiert...).

@BlackJack, vielen Dank, für das korrigieren! So sieht alles wirklich viel klarer aus. Toll!
Mir fällt das richtig schwer, auf Englisch sprechende Namen zu finden, und den Code so zu strukturieren, dass er leicht zu lesen ist. Und ja, ich hatte zunächst auch "children" im Code, doch irgendwie wurde mir das zu lang. Aber ich werde wieder zurückkehren zu "children".

Also nochmal danke an Euch alle!

Ihr werdet mir sicherlich verzeihen, wenn ich in naher Zukunft trotz der Ratschläge immer mal wieder Kommandos von Abfragen (@Hyperion) nicht klar trenne, oder mich bei der Benennung meiner Funktionen, Klassen und Variablen ungeschickt anstelle ;-) .

Grüße, Ulrich

Re: Klassen: Einfache Baumstruktur, Pointer

Verfasst: Montag 18. August 2014, 21:23
von /me
losgehts hat geschrieben:[...] oder mich bei der Benennung meiner Funktionen, Klassen und Variablen ungeschickt anstelle ;-) .
Schau dir auf jeden Fall mal den Style Guide for Python Code an.

Edit: Oh, den hatte BlackJack schon genannt. Man kann ihn aber gar nicht oft genug erwähnen. :wink:

Re: Klassen: Einfache Baumstruktur, Pointer

Verfasst: Dienstag 19. August 2014, 10:23
von losgehts
Hallo /me,

jupp habe ich schon in meine Bookmarks aufgenommen.

Danke,
Ulrich