Attribute Basisklasse / Unterklasse

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
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Hallo...

Ich habe folgendes Modell:

Code: Alles auswählen

class Recurrence(object):
    def __init__(self, foo, bar):
        self.end_date = self.list_match(self._recurrence_len)

class MonthlyRecurrence(Recurrence):
    def __init__(self, foo, bar):
        Recurrence.__init__(self, foo, bar)

    def _recurrence_len(self):
        return monthly_foo

YearlyRecurrence(MonthlyRecurrence):
    def __init__(self, foo, bar):
        MonthlyRecurrence.__init__(self, foo, bar)

    def _recurrence_len(self):
        return yearly_foo
Wenn ich nun eine Instanz von YearlyRecurrence() erstelle, wird ja die __init__ von MonthlyRecurrence() aufgerufen, die wiederum die __init__ von Recurrence() aufruft, innerhalb der dann self._recurrence_len(), in dem Fall also Monthly._recurrence_len() abgefragt wird.
Wie kann ich es erreichen, dass innerhalb der Recurrence.__init__() die self._recurrence_len() in Abhängigkeit der "untersten" Klasse aufgerufen wird. Bei einer YearlyRecurrence()-Instanz eben die YearlyRecurrence._recurrence_len(), bei einer MonthlyRecurrence()-Instanz die MonthlyRecurrence._recurrence_len().
Bringt mich hier super() weiter? Oder ist gar meine Konstruktion so nicht optimal?
:K

Gruß
mutetella
Zuletzt geändert von mutetella am Freitag 12. November 2010, 10:44, insgesamt 1-mal geändert.
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Mal abgesehen davon, dass du in deinem Beispiel nie "_recurrence_len" aufrufst, aber dein gewünschtes Verhalten ist das Standardverhalten. Also ist dein Code entweder nicht der, welcher den Fehler erzeugt oder du interpretierst deine Ergebnisse falsch.
Das Leben ist wie ein Tennisball.
ws
User
Beiträge: 65
Registriert: Freitag 16. Juni 2006, 13:19

Hallo Mutetella,

wenn ich das richtig sehe, muss Du dafür gar nix tun. Pythons Methoden sind erstmal standardmässig virtuell, d.h. sie werden von abgeleiteten Implementierungen ggf. überschrieben. Erzeugst Du eine Instanz von YearlyRecurrence, ändert die Tatsache, dass Du im Initializer noch MonthlyRecurrence.__init__ aufrufst, nichts an der Tatsache, dass Deine Instanz vom Typ YearlyRecurrence ist. Python ruft dann automatisch die Methode der am besten passenden Klasse auf "s. Python Method resolution order", was in diesem Fall YearlyRecurrence._recurrence_len() ist.

Gruss

Wolfgang
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@mutetella: Ein jährlich wiederkehrendes Ereignis ist kein monatlich wiederkehrendes Ereignis. Deine Vererbungsstruktur sollte das widerspiegeln.

Gruß,
Mick.
In specifications, Murphy's Law supersedes Ohm's.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

EyDu hat geschrieben:... dass du in deinem Beispiel nie "_recurrence_len" aufrufst, ...
Sorry, müsste natürlich

Code: Alles auswählen

class Recurrence(object):
    def __init__(self, foo, bar):
        self.end_date = self.list_match(self._recurrence_len())
heissen...
ws hat geschrieben:Python ruft dann automatisch die Methode der am besten passenden Klasse auf "s. Python Method resolution order", was in diesem Fall YearlyRecurrence._recurrence_len() ist.
Jepp, wie mir soeben aufgefallen ist, liegt der Fehler auch an anderer Stelle:

Beide Klassen, sowohl MonthlyRecurrence() wie auch YearlyRecurrence() legen beim Instanziieren ein Attribut self.interval an. Beim Instanziieren von YearlyRecurrence() erhält die Basisklasse als interval-Attribut immer den Wert 1. 'interval' wird dann in der jeweiligen _recurrence_len() zur Berechnung verwendet. Um das ganze zu vereinfachen, habe ich den Ablauf mal als kleines Modell abgebildet:

Code: Alles auswählen

class Recurrence(object):
    def __init__(self, interval):
        self.interval = interval
        print(self.get_interval())
        
class MonthlyRecurrence(Recurrence):
    def __init__(self, interval):
        First.__init__(self, interval)
        self.interval = interval
        print(self.get_interval())

    def get_interval(self):
        return self.interval

class YearlyRecurrence(MonthlyRecurrence):
    def __init__(self, interval):
        self.interval = interval
        Second.__init__(self, 1)
        print(self.get_interval())

    def get_intervclass Recurrence(object):
    def __init__(self, interval):
        self.interval = interval
        print(self.get_interval())
Warum erhalte ich hier folgende Ausgabe?

Code: Alles auswählen

In [176]: y = YearlyRecurrence(3)
1
1
1

In [177]: y.interval
Out[177]: 1
pillmuncher hat geschrieben:Ein jährlich wiederkehrendes Ereignis ist kein monatlich wiederkehrendes Ereignis.
Monatliche Wiederholung: Jeden 1. und 15. des Monats
Jährliche Wiederholung: Alle 2 Jahre am 1. und 15. Januar im Januar und März
Ich habe also in einer jährlichen Wiederholung lediglich den Jahresinterval und die Angabe von Monaten. Ich war da jetzt ganz pragmatisch und hab' von MonthlyRecurrence() geerbt, damit ich die Abfrage nur noch um den Jahresinterval und die Monate erweitern muss. Hier der ganze Code:
http://paste.pocoo.org/show/289815/
Vielleicht hast Du ja Lust, da mal drüberzuschauen.

Gruß
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Es fehlt irgendwie noch, was du als Ausgabe erwartest ;-) Ich vermute mal, dass du als Ausgabe eine 3 erwartest. Dann solltest du dir über die Reihenfolge von

Code: Alles auswählen

def __init__(self, interval):
        self.interval = interval
        Second.__init__(self, 1)
Gedanken machen.
Das Leben ist wie ein Tennisball.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Also erstmal sorry für meine schlampige Fragestellung und das fehlerhafte Codebeispiel... :oops: Das mit der Reihenfolge war natürlich ein ganz doofer Fehler! Und von Recurrence() erben zu wollen, in der __init__() aber Second() aufzurufen zeugt auch nicht gerade von einem klaren Verstand... :-)

Hier also nochmal das jetzt korrekte Beispiel:

Code: Alles auswählen

class Recurrence(object):
    def __init__(self, interval):
        self.interval = interval
        print(self.get_interval())
        
class MonthlyRecurrence(Recurrence):
    def __init__(self, interval):
        Recurrence.__init__(self, interval)
        self.interval = interval
        print(self.get_interval())

    def get_interval(self):
        return 'MonthlyRecurrence: %i' % self.interval

class YearlyRecurrence(MonthlyRecurrence):
    def __init__(self, interval):
        MonthlyRecurrence.__init__(self, 1)
        self.interval = interval
        print(self.get_interval())

    def get_interval(self):
        return 'YearlyRecurrence: %i' % self.interval
Als Ausgabe erhalte ich:

Code: Alles auswählen

In [206]: y = YearlyRecurrence(3)
YearlyRecurrence: 1
YearlyRecurrence: 1
YearlyRecurrence: 3
Ich verstehe nicht, dass, auch wenn immer die YearlyRecurrence.get_interval() aufgerufen wird, dabei nicht immer die self.interval der YearlyRecurrence()-Instanz verwendet wird. Selbst wenn ich die YearlyRecurrence.__init__() folgendermaßen ändere...

Code: Alles auswählen

def __init__(self, interval):
        self.interval = interval
        MonthlyRecurrence.__init__(self, 1)
        self.interval = interval
        print(self.get_interval())
...verwendet YearlyRecurrence.get_interval() beim 1. und 2. Aufruf die self.interval aus MonthlyRecurrence(), erst beim 3. Aufruf aus YearlyRecurrence() wird dann auch die self.interval aus YearlyRecurrence() ausgegeben.

Als ich diesen Thread eröffnete, dachte ich noch, dass die "falschen" Methoden aufgerufen werden. An der Ausgabe ist aber zu sehen, dass die aufgerufene Methode richtigerweise die Methode aus YearlyRecurrence() ist. Nur das verwendete Attribut nicht.

Warum ist das so?

Gruß
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
DaMutz
User
Beiträge: 202
Registriert: Freitag 31. Oktober 2008, 17:25

mutetella hat geschrieben:Warum ist das so?
ganz einfach :) :
- YearlyRecurrence.__init__ wird aufgerufen
- MonthlyRecurrence.__init__ wird aufgerufen
- Recurrence.__init__ wird aufgerufen
- self.interval wird auf 1 gesetzt (in Recurrence.__init__)
- self.interval wird ausgegeben (in YearlyRecurrence.get_interval) -> 1
- self.interval wird auf 1 gesetzt (in MonthlyRecurrence.__init__)
- self.interval wird ausgegeben (in YearlyRecurrence.get_interval) -> 1
- self.interval wird auf 3 gesetzt (in YearlyRecurrence.__init__)
- self.interval wird ausgegeben (in YearlyRecurrence.get_interval) -> 3

Verstehst du es jetzt?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Das ist nicht unerwartet. ich mache es mal schrittweise:
1. Du rufst die __init__ von YearlyRecurrence auf
2. Darin rufst du die __init__ von MonthlyRecurrence mit dem Wert 1 auf
3. Nun wird die __init__ von Recurrence aufgerufen, wieder mit dem Wert 1
4. self.interval setzt du nun auf 1
5. Jetzt rufst du self.get_interval auf, welches natürlich nun 1 ausgibt
6. Du fällst zurück in die __init__ von MonthlyRecurrence und setzt self.interval erneut auf 1
7. Nun gibt self.get_interval natürlich 1 aus
8. Nun kommst du zurück in die __init__ von YearlyRecurrence und set self.interval auf 3, da diese an den Parameter interval gebunden ist
9. In self.get_interval wird nun korrekt die 3 ausgegeben

Edit: Da war ich wohl zu langsam
Das Leben ist wie ein Tennisball.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Und welche Möglichkeit habe ich, in der YearlyRecurrence()-Instanz interval auf die 3 zeigen zu lassen und in der Basisklasse MonthlyRecurrence() auf die 1?

Ich ging davon aus, dass die Basisklasse einen eigenen Namensraum hat. Ist nicht so?

Gruß
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

mutetella hat geschrieben:Und welche Möglichkeit habe ich, in der YearlyRecurrence()-Instanz interval auf die 3 zeigen zu lassen und in der Basisklasse MonthlyRecurrence() auf die 1?
Gar nicht. Du hast ein Problem mit dem Konzept. Du hast nur eine einzige Instanz.

Was du möchtest sind entweder unterschiedliche Attributnamen für unterschiedliche Zwecke oder eine Liste in YearlyRecurrence die Instanzen vom Typ MonthlyRecurrence aufnimmt.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

/me hat geschrieben:Du hast ein Problem mit dem Konzept.
Hab' ich dauernd... :D
/me hat geschrieben:... entweder unterschiedliche Attributnamen für unterschiedliche Zwecke oder eine Liste in YearlyRecurrence ...
Find' ich jetzt beides nicht wirklich "chic". Letztlich handelt es sich um 2 MonthlyRecurrence()-Methoden, die ich auch bei YearlyRecurrence()-Instanzen benötige. Wenn ich mir das jetzt so überlege, ist das überhaupt total idiotisch, deswegen von MonthlyRecurrence() zu erben.
Was würdest Du machen?

Soll ich
  • a) die beiden Methoden als Funktionen auslagern oder
    b) in die Basisklasse Recurrence() verlegen, auch wenn die Methoden dann nur von MonthlyRecurrence()- und YearlyRecurrence()-Instanzen genutzt werden, nicht aber von DailyRecurrence()- und WeeklyRecurrence()-Instanzen, die auch von Recurrence() erben. Wäre das ok? Oder sollten Basisklassen nicht ausschließlich Methoden bereitstellen, die dann auch von allen Unterklassen benötigt werden? Oder
    c) ich lasse das mit den verschiedenen Klassen und verwende eine Idee von bwbg, die nicht jeden Wiederholungstyp in eine separate Klasse steckt, sondern Wiederholungen gewissermaßen aus einer Sammlung diverser Prüf-Funktionen bildet, siehe hier.
Gruß
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Antworten