Seite 1 von 2

[gelöst] Anfänger Problem mit vererben, klassen und python

Verfasst: Freitag 11. Januar 2008, 14:58
von würmchen
Hi Leute,
also ich hab eigentlich schon ein wenig programmiererfahrung und bin eigentlich auch mit OOP vertraut, aber was python gerade mit mir macht ist echt zuviel :-)

Code: Alles auswählen

class Query(object):
    do_evaluation = 0
    print_layout = 0
    column_name = 0
    search_table = 0

    def __init__(self, evaluate = 0, layout = 0, **y):
        type(self).y = y
        type(self).do_evaluation = evaluate
        type(self).print_layout = layout
        type(self).column_name
        type(self).search_table
           
    def __del__(self):
        pass
           
    def returnLayout(self):
        if (type(self).print_layout == 0): # das raff ich nicht?!
            print self.form_layout

class PdbKey(Query):
    def __init__(self, evaluate = 0, layout = 0):
        Query.__init__(self, evaluate, layout)
        self.column_name = "id"
        self.search_table = "MMS_ENTRY"
        self.form_layout = "No idea at the moment, sorry"

    def __del__(self):
        pass
           
    def evaluate(this):
        pass

def main():
    test = PdbKey(evaluate = 1) 
    test.returnLayout()

if __name__ == '__main__':
    main()
Also das ist mein Beispiel an dem ich gerade rumprobiere und ich raffe nichts mehr mit type(self) und die art und weise wie der konstruktor aufgerufen wird und und und....

ich arbeite mit dem openbook von galileo computing....

was ich dachte ist, das ich jetzt meine Klass pdbkey von query ableite und ich somit alle methoden die ich in query schon genutzt habe auch in pdbkey nutzen kann.
nu erstelle ich ein objekt pdbkey und übergebe ihm bei evaluate eine 1... aber irgendwie habe ich kein plan, wie ich die methode die diese variable überprüfen soll dazu bekomme die richtige variable zu prüfen... ich hab da ständig eine 0 drin stehen...

ist sicher nur ne kleinigkeit und ein denkfehler...
vielleicht hat jemand zeit für mich anfänger...

Re: Anfänger Problem mit vererben, klassen und python ;-)

Verfasst: Freitag 11. Januar 2008, 15:21
von Leonidas
Ich habe zwar nicht direkt eine Ahnung was du da eigentlich machen willst, da ich den Sinn der Übung nicht verstehe, aber:
würmchen hat geschrieben:ich arbeite mit dem openbook von galileo computing....
Nimm dir ein besseres Buch. Das Openbook - diplomatisch formuliert - keine gute Lektüre, von allen ungelungenen Kapiteln ist das zu OOP am schlimmsten.
Besser ist "How to think like a computer scientist" und "A Byte of Python", die es beide auch auf Deutsch gibt.

Verfasst: Freitag 11. Januar 2008, 15:26
von würmchen
Ich will eigentlich nur die variable, die in pdbkey gesetzt wurde beim erstellen mit einer methode der elternklasse abfragen, bzw wenn die variable der kinderklasse eben 1 ist etwas auszugeben...

Hm, ich werd mir mal a byte of python reinknüppeln, mal sehn ob ich damit weiter komme...

Verfasst: Freitag 11. Januar 2008, 15:29
von Leonidas
würmchen hat geschrieben:Hach, ich will was programmieren und wurde gezwungen es in der Sprache zu machen und ich denke ich brauch noch ein wenig eingewöhnungszeit...
Das ist ja kein Problem. Wenn du auch sagst, was du für ein aktuelle Problem hast, dann können wir dir womöglich sogar helfen :)

Wie gesagt, der Code macht verschiedene komische Sachen, aber wenn du sagst as du damit erreichen willst, gibt es auch für dich Hoffnung ;)

Verfasst: Freitag 11. Januar 2008, 15:35
von würmchen
hm, hast mir zu schnell geantwortet ;-) hatte meinen beitrag nochmal editiert....
Ich will eigentlich nur die variable, die in pdbkey gesetzt wurde beim erstellen mit einer methode der elternklasse abfragen, bzw wenn die variable der kinderklasse eben 1 ist etwas auszugeben...
Ich frag mich eben ob es möglich ist mit einer methode der elternklasse eine variable der kindklasse zu prüfen, dachte das geht, aber es scheint nicht zu gehen... muss wohl die variable mit übergeben wenn ich den construktor der elternklasse innerhalb des kindkonstruktors aufrufe....

hach, hoffentlich versteht man mich ;-)

Verfasst: Freitag 11. Januar 2008, 15:37
von Rebecca
würmchen hat geschrieben:Ich will eigentlich nur die variable, die in pdbkey gesetzt wurde beim erstellen mit einer methode der elternklasse abfragen, bzw wenn die variable der kinderklasse eben 1 ist etwas auszugeben...
So etwa?

Code: Alles auswählen

class Query(object):

    def __init__(self, y):
        self.y = y
        
    def returnLayout(self):
        if self.y == 1:
            print "Eins"

class PdbKey(Query):
    def __init__(self, y):
        Query.__init__(self, y)


def main():
    test = PdbKey(y=1)
    test.returnLayout()
    
    test2 = PdbKey(y=2)
    test2.returnLayout()

if __name__ == '__main__':
    main()

Verfasst: Freitag 11. Januar 2008, 15:48
von würmchen
Rebecca hat geschrieben: So etwa?

Ok, nur will ich jetzt wissen, ob ich unbedingt die variable, in deinem fall y, an meinen query konstruktor übergeben muss? ich dachte ich könnte dann einfach die methode returnLayout so nutzen als ob ich sie in der kindklasse erstellt hätte....

Verfasst: Freitag 11. Januar 2008, 15:55
von BlackJack
@würmchen: Zu den "komischen" Sachen gehört zum Beispiel ``type(self).y = y``. Was soll dass denn bewirken?

Und vergiss am besten, dass es `__del__()` gibt. Die Methode ist nicht wirklich brauchbar, da nicht zuverlässig. Und kann unter bestimmten Umständen, alleine durch ihre Existenz, sogar verhindern, dass Objekte von "garbage collector" freigegeben werden können.

Zur letzten Frage: Du könntest auch in `PdbKey` das `y` an `self.y` binden, das ist aber letztendlich unsauberer, weil es dann keine Möglichkeit mehr gibt, in der Oberklasse in der `__init__()` auf diese Bindung oder den Wert Einfluss zu nehmen.

Verfasst: Freitag 11. Januar 2008, 15:57
von Rebecca
würmchen hat geschrieben:Ok, nur will ich jetzt wissen, ob ich unbedingt die variable, in deinem fall y, an meinen query konstruktor übergeben muss?
Das ist notwendig, da die __init__-Medhode (die genau genommen kein Konstruktor ist) ein Argument erwartet! Natuerlich kannst du die __init__-Medhode auch so gestalten, dass z.B. y ein Defaultwert hat, oder gar keine weiteren Argumente nimmt und einfach so das Attribut y setzt.
ich dachte ich könnte dann einfach die methode returnLayout so nutzen als ob ich sie in der kindklasse erstellt hätte....
Das hat jetzt mit der ersten Frage nichts zu tun, oder ich verstehe dich nicht... Du *kannst* doch die Methode so benuzen als haettest du sie in der kindklasse definiert?

Verfasst: Freitag 11. Januar 2008, 16:04
von würmchen
Rebecca hat geschrieben:
ich dachte ich könnte dann einfach die methode returnLayout so nutzen als ob ich sie in der kindklasse erstellt hätte....
Das hat jetzt mit der ersten Frage nichts zu tun, oder ich verstehe dich nicht... Du *kannst* doch die Methode so benuzen als haettest du sie in der kindklasse definiert?
Ja, aber doch nur wenn ich die bestimmte variable nochmal an query weitergebe... also mit Query.__init__(self, y)

ich hab mir vorgestellt das ich returnLayout einfach nutzen kann, ohne das ich in der Query Klasse ein y definiert habe, weil das gibts dann ja in der kindklasse...

ich mein, letztendlich ist es ja nicht mehr speicheraufwand, weil es wird ja kein neuer speicher dafür abgelegt, sondern die speicheradresse übergeben, aber ich hab dadurch mehr schreibarbeit.... :-)

BlackJack hat geschrieben:@würmchen: Zu den "komischen" Sachen gehört zum Beispiel ``type(self).y = y``. Was soll dass denn bewirken?
Das is was ich in dem Galileobuch gelesen habe aber nicht verstanden habe... Wenn es interessiert kann ich das hier mal posten...
Und, ok, die __del__ methode ist schon gestorben ;-)

Verfasst: Freitag 11. Januar 2008, 16:11
von Rebecca
würmchen hat geschrieben:ich hab mir vorgestellt das ich returnLayout einfach nutzen kann, ohne das ich in der Query Klasse ein y definiert habe, weil das gibts dann ja in der kindklasse...
Vererbung funktioniert genau andersherum. Eine Klasse hat Informationen ueber ihre Elternklasse, aber eine Elternklasse weiss nichts von ihren Kindklassen.

Wenn du auf ein Attribut y einer Klasse zugreifst, wird zuerst geschaut, ob die Klasse selbst dieses Attribut hat, danach wird geschaut, ob die Elternklassen dieses Attribut haben etc. Du kannst also in Kindklassen auf Attribute zugreifen, die nur in der Elternklasse vorhanden sind, aber nicht umgekehrt.

Verfasst: Freitag 11. Januar 2008, 16:16
von BlackJack
@würmchen: Wenn Du nicht weisst was das tut, dann ist das "programming by accident", funktioniert halt zufällig. Wobei ich mir bei dem Quelltext und deinem letzten Beitrag auch nicht so ganz sicher bin, dass Du Klassen und Instanzen ordentlich auseinander hältst und das es wirklich so funktioniert wie es soll.

So wie das da jetzt steht hat `Query` 4 Klassenvariablen und keine einzige Instanzvariable. Sprich *alle* Instanzen von `Query` teilen sich die gleichen Werte. Und das `y` wird zu einer Klassenvariablen von der Unterklasse von der aus die `Query.__init__()` aufgerufen wird. Das ist eine sehr seltsame Konstruktion um es mal vorsichtig aus zu drücken.

Verfasst: Freitag 11. Januar 2008, 16:31
von würmchen
BlackJack hat geschrieben:@würmchen: Wenn Du nicht weisst was das tut, dann ist das "programming by accident", funktioniert halt zufällig. Wobei ich mir bei dem Quelltext und deinem letzten Beitrag auch nicht so ganz sicher bin, dass Du Klassen und Instanzen ordentlich auseinander hältst und das es wirklich so funktioniert wie es soll.

So wie das da jetzt steht hat `Query` 4 Klassenvariablen und keine einzige Instanzvariable. Sprich *alle* Instanzen von `Query` teilen sich die gleichen Werte. Und das `y` wird zu einer Klassenvariablen von der Unterklasse von der aus die `Query.__init__()` aufgerufen wird. Das ist eine sehr seltsame Konstruktion um es mal vorsichtig aus zu drücken.

Ok, ich kopier den Abschnitt aus dem Buch mal hier rein....
Was geplant ist, ist eine Zählerklasse zu bauen, die dann später an andere Klassen vererbt werden kann. Gezählt wird, wieviele Instanzen einer Klasse existieren....
Galileo Buch hat geschrieben:Wir wollen nun das angegebene Beispiel in Python implementieren, wobei wir uns zuerst der Zaehler-Klasse zuwenden:

Code: Alles auswählen

class Zaehler(object): 
    Anzahl = 0 
 
    def __init__(self): 
        type(self).Anzahl += 1 
 
    def __del__(self): 
        type(self).Anzahl -= 1
Galileo Buch hat geschrieben:Die Definition enthält bis auf den Zugriff auf das Attribut Anzahl mittels type(self) nichts Neues. Wir können deshalb nicht mehr direkt über den Klassennamen per Zaehler.Anzahl auf das Attribut zugreifen, weil wir von der Klasse erben wollen und die Subklassen jeweils ihr eigenes statisches Attribut Anzahl haben sollen. Würden wir mit Zaehler.Anzahl arbeiten, könnten wir damit die Gesamtanzahl der Konto- und Angestellter-Instanzen berechnen. Mithilfe von type lässt sich der Datentyp einer Instanz ermitteln, und das nutzen wir, um den Zähler abhängig davon, welchen Typ self hat, für die richtige Klasse zu ändern
Galileo Buch hat geschrieben:Nun wollen wir unsere Konto-Klasse von Zaehler erben lassen:

Code: Alles auswählen

class Konto(Zaehler): 
    def __init__(self, inhaber, kontonummer, kontostand, 
                       max_tagesumsatz=1500):

        Zaehler.__init__(self) # Wichtige Zeile - siehe unten

        self.__Inhaber = inhaber 
        self.__Kontonummer = kontonummer 
        self.__Kontostand = kontostand 
        self.__MaxTagesumsatz = max_tagesumsatz 
        self.__UmsatzHeute = 0 
 
    # hier wären die restlichen Methoden
Galileo Buch hat geschrieben:Im Wesentlichen haben sich bei der neuen Definition von Konto nur das schon angesprochene Ersetzen von object durch Zaehler und die erste Zeile des Konstruktors geändert. Mit Zaehler.__init__(self) rufen wir den Konstruktor der Basisklasse auf, um unser Konto auch als Zähler benutzen zu können. Dies ist deshalb notwendig, weil eine Klasse nur eine Methode __init__ haben kann. Bei der Vererbung tritt nun oft der Fall ein, dass die erbende Klasse Methoden definiert, die auch schon in der Basisklasse vorhanden waren – in unserem Beispiel eben der Konstruktor __init__. In einem solchen Fall werden die Methoden der Basisklasse mit denen, die die Subklasse selbst definiert, überschrieben, sodass im Beispiel self.__init__ eine Referenz auf den Konstruktor von Konto und nicht auf den von Zaehler enthält. Um trotzdem auf solche überschriebenen Methoden zugreifen zu können, ersetzt man beim Aufruf das self vor dem Punkt durch den Namen der entsprechenden Basisklasse und übergibt self explizit als Parameter. Würde Zaehler.__init__ noch weitere Parameter erwarten, so würden diese wie üblich durch Kommata getrennt dahinter geschrieben.
Und wenn ich das was die da gemacht haben richtig verstanden habe, benutzen die dieses type(this) Konstrukt um die Klassenvariable irgendwie als Instanzvariable zu nutzen.

Im Großen und ganzen sind mir aber die Unterschiede zwischen Instanz und Klasse schon bewusst. Nur dieses Beispiel hat mich verwirrt.

Verfasst: Freitag 11. Januar 2008, 16:42
von BlackJack
Das wird nicht benutzt um aus Klassenvariablen Instanzvariablen zu machen, sondern um in einer Oberklasse Attribute an die Unterklasse zu binden, von der die Instanz erzeugt wurde. Nicht an diese Instanz sondern an deren Klasse. Ein sehr exotischer Spezialfall. Genau wie dieser Zähler, der sowieso nicht zuverlässig funktioniert, zumindest das runter zählen, wegen der `__del__()`-Methode.

Dass das Bucht nichts taugt, wurde hier schon einmal erwähnt, oder? :-)

Verfasst: Freitag 11. Januar 2008, 16:53
von würmchen
BlackJack hat geschrieben:Das wird nicht benutzt um aus Klassenvariablen Instanzvariablen zu machen, sondern um in einer Oberklasse Attribute an die Unterklasse zu binden, von der die Instanz erzeugt wurde. Nicht an diese Instanz sondern an deren Klasse. Ein sehr exotischer Spezialfall. Genau wie dieser Zähler, der sowieso nicht zuverlässig funktioniert, zumindest das runter zählen, wegen der `__del__()`-Methode.

Dass das Bucht nichts taugt, wurde hier schon einmal erwähnt, oder? :-)
Ja, wurde schon gesagt, und selbst mir als anfänger ist ein wunderbarer gegensatz in dem Buch aufgefallen... Die hatten folgende code untereinander...

Code: Alles auswählen

class Beispielklasse(object): 
    def __init__(self): 
        print "Hier spricht der Konstruktor" 
 
    def __del__(self): 
        print "Und hier kommt der Destruktor"

Code: Alles auswählen

>>> obj = Beispielklasse() 
Hier spricht der Konstruktor 
>>> del obj 
Und hier kommt der Destruktor

Code: Alles auswählen

>>> v1 = Beispielklasse() 
Hier spricht der Konstruktor 
>>> v2 = v1 
>>> del v1 
>>> del v2 
Und hier kommt der Destruktor
Darunter stand dann
Wie Sie sehen, wurde __del__ einmalig nach dem zweiten del-Statement aufgerufen und nicht zweimal. Dies wird auch dann noch einmal klar, wenn man sich vor Augen hält, dass ein Objekt zum Entfernen erst einmal erzeugt werden muss: Für einen Konstruktor-Aufruf gibt es genau einen Destruktor-Aufruf desselben Objekts.
Weiter unten im Buch schreiben sie dann...

Code: Alles auswählen

class Konto(object): 
    Anzahl = 0 # Zu Beginn ist die Instanzanzahl 0 
 
    def __init__(self, inhaber, kontonummer, kontostand, 
                       max_tagesumsatz=1500): 
        self.__Inhaber = inhaber 
        self.__Kontonummer = kontonummer 
        self.__Kontostand = kontostand 
        self.__MaxTagesumsatz = max_tagesumsatz 
        self.__UmsatzHeute = 0 
        Konto.Anzahl += 1 # Instanzzähler erhöhen 
 
    def __del__(self): 
        Konto.Anzahl -= 1 
Und folgenden weiteren code...

Code: Alles auswählen

>>> k1 = Konto("Florian Kroll", 3111987, 50000.0) 
>>> k2 = Konto("Lucas Hövelmann", 25031988, 43000.0) 
>>> k3 = Konto("Sebastian Sentner", 6091987, 44000.0) 
>>> Konto.Anzahl 
3 
>>> k1.Anzahl 
3 
>>> del k2 
>>> Konto.Anzahl 
2 
>>> del k1 
>>> k3.Anzahl 
1 
>>> del k1 
>>> del k3 
>>> Konto.Anzahl 
0
Wiso wird der Zähler runter gezählt, weil die doch oben gesacht haben, der del block wird nur bei der letzten instanz ausgeführt.....

Oder überseh ich was...
Also für mich war das beim lesen ein echter Gegensatz....