Button auslesen

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.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

Sirius3 hat geschrieben: Freitag 10. September 2021, 11:44 Die Dokumentation ist ganz sicher falsch, denn Basisklassen werden über BasisKlasse.__init__(...) aufgerufen: https://github.com/python/cpython/blob/ ... _.py#L2649
Unter Python2 hat super() mit Tkinter nicht funktioniert. Wenn ich mich recht entsinne (ich bin zu faul, das jetzt nochmal zu recherchieren, zumal Python2 ja ohnehin EOL ist), dann war das deswegen, weil Frederik Lundhs Tkinter-Klassen nicht von object geerbt haben, was aber die Voraussetzung für die Verwendung von super() war. Heute sind diese expliziten Aufrufe nicht mehr notwendig, denn in Python3 erben alle Klassen ohnehin implizit von object und erfüllen damit die Voraussetzungen, die super() braucht. Insofern sind also sowohl der alte Code als auch die neue Dokumentation absolut korrekt.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@LukeNukem: Das stimmt so nicht. Von `object` zu erben ist eine notwendige Bedingung, die alleine reicht aber nicht aus. Es müssen auch alle beteiligten Methoden selbst `super()` benutzen. Sonst funktioniert `super()` nur so halb, also eben nicht so super. Der Name ist auch irreführend. Das ruft nicht zwingend die Methode in der Superklasse auf, sondern die nächste Methode in der „method resolution order“ (MRO). In der Programmiersprache Dylan, wo das her kommt, heisst das deswegen auch `next-method` und nicht `super`.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

__blackjack__ hat geschrieben: Samstag 11. September 2021, 15:44 @LukeNukem: Das stimmt so nicht.
Da ich das mit dem OOP-Tkinter schon seit ziemlich langer Zeit genau so mache, wie es auch die Dokumentation von Python3 zeigt, und ich damit noch nie ein Problem gehabt habe, gehe ich weiterhin davon aus, daß die Dokumentation korrekt ist und demzufolge auch meine Ausführungen dazu korrekt sind. Allerdings kannst Du mich gerne überzeugen, indem Du ein lauffähiges Codebeispiel postest, welches zeigt, daß "super().<methode>(*args, **kwargs)" tatsächlich etwas anderes tut als "Basisklasse.<methode>(self, *args, **kwargs)". Viel Erfolg! ;-)
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@LukeNukem:

Code: Alles auswählen

#!/usr/bin/env python3
from tkinter import Tk, Button


class SomeMixin:
    def __init__(self, *args, **kwargs):
        print("SomeMixin init")
        super().__init__(*args, **kwargs)


class SpecializedButton(Button, SomeMixin):
    def __init__(self, *args, **kwargs):
        print("SpecialButton init")
        super().__init__(*args, **kwargs)


def main():
    root = Tk()
    button = SpecializedButton(root)
    button.pack()
    root.mainloop()


if __name__ == "__main__":
    main()
Die `SomeMixin.__init__()` wird nicht aufgerufen. Und zwar weil `Button` kein `super()` benutzt. Damit `super()` so funktioniert wie es soll, muss wie schon mal gesagt, jede betroffene Methode `super()` verwenden. Sonst kann `super()` nicht das machen wofür es gedacht ist.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

__blackjack__ hat geschrieben: Samstag 11. September 2021, 18:07 Die `SomeMixin.__init__()` wird nicht aufgerufen. Und zwar weil `Button` kein `super()` benutzt. Damit `super()` so funktioniert wie es soll, muss wie schon mal gesagt, jede betroffene Methode `super()` verwenden. Sonst kann `super()` nicht das machen wofür es gedacht ist.
Das hat aber nichts mit Tkinter zu tun, sondern mit Mehrfachvererbung und den subtilen Spezialitäten, wie super() die MRO abarbeitet: die wird nämlich von links nach rechts durchsucht, und wenn ein Attribut des gewünschten Namens vorhanden ist, eben dieses benutzt. Dieser Code hier hat dasselbe "Problem", obwohl Tkinter offensichtlich gar nicht involviert ist UND alle __init__()-Methoden super().__init__() aufrufen:

Code: Alles auswählen

#!/usr/bin/env python

class P:
    def __init__(self):
        print('      P.__init__()')
        super().__init__()

class F(P):
    def __init__(self):
        print('    F.__init__()')
        super().__init__()
        
class A(F):
    def __init__(self):
        print('  A.__init__()')
        super().__init__()

class B(F):
    def __init__(self):
        print('  B.__init__()')
        super().__init__()
        
class C(A, B):
    def __init__(self):
        print('C.__init__()')
        super().__init__()

        
if __name__ == '__main__':
    c = C()

Code: Alles auswählen

C.__init__()
  A.__init__()
  B.__init__()
    F.__init__()
      P.__init__()
Du siehst: es ist alles eine Frage der MRO, und super() ist nicht dazu da, explizite Aufrufe von Methoden der Parent-Klasse(n) zu ersetzen, wie Tkinter und etliche andere Libraries mit Mehrfachvererbung sie benutzen. Aber Mehrfachvererbung ist in jeder Sprache, die ich kenne und die sie unterstützt, eine recht komplexe Angelegenheit, selten nötig -- deswegen unterstützen manche Sprachen auch gar keine Mehrfachvererbung -- und eher etwas für Leute, die wissen, was sie tun. Übrigens, da Du schon Mixins erwähnst: ein Mixin ist nach meinem Verständnis im Kern eine Klasse, die zu vorhandenen Klassen ein paar Convenience-Methoden oder -Attribute hinzufügt, und gerade nicht dazu gedacht, vorhandene Methoden oder Attribute anderer Klassen zu überschreiben wie in Deinem Beispiel, aber das nur am Rande bemerkt.

Aber, wie gesagt: alles eine Frage der MRO. Insofern läßt sich das Problem aus Deinem Codebeispiel ganz einfach lösen, indem Du stattdessen:

Code: Alles auswählen

class SpecializedButton(SomeMixin, Button):
schreibst. Dann funktioniert alles wie gewünscht.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@LukeNukem: Der Code von Dir hat ja offensichtlich *nicht* das selbe Problem, denn da werden alle `__init__()`-Methoden aufgerufen, und zwar egal in welcher Reihenfolge man die Basisklassen hinschreibt. Genau dafür ist `super()` ja da, und genau das klappt nur *immer* wenn alle `super()` benutzen.

Das umdrehen der Reihenfolge der Basisklassen funktioniert in meinem Beispiel auch nur solange `SomeMixin.__init__()` auch `super()` verwendet. Wenn man das da weg liesse, würde es auch wieder nicht gehen. Es müssen halt alle beteiligten Methoden `super()` verwenden, damit die Methoden in der MRO alle aufgerufen werden können.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Es ist halt wichtig zu verstehen, wie die Serialisierung erfolgt. Nehmen wir das folgende abgewandelte Beispiel:

Code: Alles auswählen

class P:
    def __init__(self):
        print('      P.__init__()')
        super().__init__()

class F(P):
    def __init__(self):
        print('    F.__init__()')
        super().__init__()
        
class Q:
    def __init__(self):
        print('    Q.__init__()')
        super().__init__()
        
class A(Q):
    def __init__(self):
        print('  A.__init__()')
        super().__init__()

class B(F):
    def __init__(self):
        print('  B.__init__()')
        super().__init__()
        
class C(A, B):
    def __init__(self):
        print('C.__init__()')
        super().__init__()

        
if __name__ == '__main__':
    c = C()

Mit dem Ergebnis:

Code: Alles auswählen

C.__init__()
  A.__init__()
    Q.__init__()
  B.__init__()
    F.__init__()
      P.__init__()


Nun deaktivieren wir den Aufruf von super() in Q, so wie es analog in vielen tkinter-Klassen erfolgt:

Code: Alles auswählen

class P:
    def __init__(self):
        print('      P.__init__()')
        super().__init__()

class F(P):
    def __init__(self):
        print('    F.__init__()')
        super().__init__()
        
class Q:
    def __init__(self):
        print('    Q.__init__()')
        #super().__init__()
        
class A(Q):
    def __init__(self):
        print('  A.__init__()')
        super().__init__()

class B(F):
    def __init__(self):
        print('  B.__init__()')
        super().__init__()
        
class C(A, B):
    def __init__(self):
        print('C.__init__()')
        super().__init__()

        
if __name__ == '__main__':
    c = C()

und das Ergebnis schaut auf einmal ganz anders aus

Code: Alles auswählen

C.__init__()
  A.__init__()
    Q.__init__()

da der Aufruf von object.super() in Q unterbrochen wurde.

Ich bin durchaus ein Verfechter von super(), aber man muß eben wissen, wie die MRO-Serialisierung funktioniert. Bei rein linearen Vererbungen klappt super() immer, auch wenn es falsch eingesetzt wird.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

kbr hat geschrieben: Samstag 11. September 2021, 20:55 Nun deaktivieren wir den Aufruf von super() in Q, so wie es analog in vielen tkinter-Klassen erfolgt:
Naja, das ist aus meiner Sicht leider nicht ganz richtig. Tkinter ruft die Methoden (bzw. Konstruktoren) der Elternklassen (je nach Bedarf) nicht über super(), sondern explizit auf, was im Zweifelsfall ohnehin immer die sicherste und zuverlässigste Möglichkeit ist. Sie werden also aufgerufen, und das funktioniert wunderbar. Daran ändert sich auch nichts, wenn von Tkinters Klassen erbende Kindklassen für den Aufruf von Elternmethoden super() benutzen -- darauf muß man nur achten, wenn man selbst Mehrfachvererbung benutzt, woran __blackjack__ und freundlicherweise noch einmal erinnert hat. Am Ende hat das aber, wie gesagt, nichts mit Tkinter zu tun, sondern mit Mehrfachvererbung, der MRO und der Spezialitäten von super() -- das sich trotz allem immer noch wundernbar mit Tkinter nutzen läßt.

Am Ende spielt es für den Punkt, den ich ursprünglich adressiert habe, aber ohnehin keine Rolle: dabei ging es nämlich darum, daß viel zu viele Tutorials und damit auch viel zu viele Anwender Tkinter in dieser häßlichen, unübersichtlichen und kontraproduktiven prozeduralen Weise einsetzen. Dabei kann die Objektorientierung ihre Stärken gerade auch bei der Entwicklung von GUI-Software besonders gut ausspielen und für eine wesentlich bessere Codeorganisation, viel mehr Übersichtlichkeit und eine höhere Wiederverwendbarkeit sorgen.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

LukeNukem hat geschrieben: Montag 13. September 2021, 17:51 Naja, das ist aus meiner Sicht leider nicht ganz richtig. Tkinter ruft die Methoden (bzw. Konstruktoren) der Elternklassen (je nach Bedarf) nicht über super(), sondern explizit auf, was im Zweifelsfall ohnehin immer die sicherste und zuverlässigste Möglichkeit ist. Sie werden also aufgerufen, und das funktioniert wunderbar.
Es funktioniert nicht wunderbar, da die Methoden der tkinter-Elternklassen ihrerseits nicht object.__init__ aufrufen; genau das hatte ich mit dem Beispiel gezeigt. Dieser Aufruf ist aber erforderlich, damit super im Falle von Mehrfachvererbung in Verbindung mit tkinter-Klassen korrekt funktionieren kann. Allein dadurch, dass der Python 2 old-style-classes tkinter-code mit Python 3 auf einmal implizit von object erbt, ändert sich gar nichts. tkinter ist schlicht und einfach nicht für Mehrfachvererbung in Verbindung mit der MRO-Serialisierung von super entworfen. Und der explizite Aufruf von Methoden der Elternklassen ist nicht die sicherste und zuverlässigste Möglichkeit, sondern lediglich die Explizite.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

kbr hat geschrieben: Montag 13. September 2021, 20:45 Es funktioniert nicht wunderbar, da die Methoden der tkinter-Elternklassen ihrerseits nicht object.__init__ aufrufen; genau das hatte ich mit dem Beispiel gezeigt. Dieser Aufruf ist aber erforderlich, damit super im Falle von Mehrfachvererbung in Verbindung mit tkinter-Klassen korrekt funktionieren kann. Allein dadurch, dass der Python 2 old-style-classes tkinter-code mit Python 3 auf einmal implizit von object erbt, ändert sich gar nichts.
Ich nehme Deinen Einwand zur Kenntnis, aber bitte sei mir nicht böse, wenn ich jetzt einmal einen ehemaligen Bundesminister des Auswärtigen zitieren muß: ich bin nicht überzeugt. Ich nutze Tkinter seit langer Zeit nur noch objektorientiert und habe, seit ich Python3 nutze, niemals Probleme mit super() gehabt. Mir geht allerdings die Hybris ab, das als Beleg werten oder gar anführen zu wollen, das wäre natürlich ein argumentativer Fehlschluß und dieses hohen Hauses unwürdig. Daß die offizielle Dokumentation zu Tkinter diese Technik ebenfalls empfiehlt, wäre insofern allenfalls ein argumentum ad populum.
kbr hat geschrieben: Montag 13. September 2021, 20:45 tkinter ist schlicht und einfach nicht für Mehrfachvererbung in Verbindung mit der MRO-Serialisierung von super entworfen.
Das sagst Du, und andere Menschen in diesem Forum ebenfalls. Mir dagegen fällt es aufgrund meiner gegenteiligen Erfahrungen und der Tatsache, daß es in der Python-Dokumentation genau so gezeigt wird, schwer, das zu glauben (ganz abgesehen davon, daß das nicht mein Punkt war, aber sei's drum). Da wir hier aber in einem Forum zu einer ausgesprochen zuverlässigen Programmiersprache sind, die sich als enorm stabil erwiesen hat und in der Regel absolut reproduzierbare Ergebnisse hervorbringt, möchte ich auch Dich sehr herzlich darum bitten, mir Deine Aussagen als Beispiel in lauffähigem Code zu demonstrieren. Und zwar bitte nicht wie Dein Vorgänger unter Mißachtung der MRO, sondern bitte an Code, der Deine Ausführungen ganz konkret an und mit Tkinter zeigt. Wenn Du das kannst, dann mache ich mir die Arbeit -- auf Wunsch natürlich mit ausdrücklicher Bezugnahme auf Dich -- einen Bugreport zu verfassen, damit die Dokumentation korrigiert wird. Lieben Dank für Deine Bemühungen!
kbr hat geschrieben: Montag 13. September 2021, 20:45 Und der explizite Aufruf von Methoden der Elternklassen ist nicht die sicherste und zuverlässigste Möglichkeit, sondern lediglich die Explizite.
Ja, genau -- und dieses Explizite macht es aus meiner Perspektive zur sichersten und zuverlässigsten Möglichkeit. super() ist etwas, das primär den "Faulpelzen" mit ihrem Streben nach wartbarem und wiederverwendbarem Code hilft. Befremdliche Pedanten wie ich, die PEP20 mögen ("explicit is better than implicit"), mögen die explizite Variante prinzipiell lieber, denn wenn man es richtig macht, funktioniert sie immer und gibt uns als Entwicklern die präziseste mögliche Kontrolle darüber, was, wann, wo und wie aufgerufen wird! ;-)
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Für andere, die in Python neu sind, der Hinweis: tkinter wurde entworfen, als es super() in Python noch nicht gab; das lässt vielleicht einige der Beiträge hier verständlicher werden. Weiter ist tkinter open-source, die Implementierung kann eingesehen werden. Und Beiträge, die genau dies getan haben, gab es hier auch schon.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

kbr hat geschrieben: Samstag 18. September 2021, 14:15 Für andere, die in Python neu sind, der Hinweis: tkinter wurde entworfen, als es super() in Python noch nicht gab; das lässt vielleicht einige der Beiträge hier verständlicher werden. Weiter ist tkinter open-source, die Implementierung kann eingesehen werden. Und Beiträge, die genau dies getan haben, gab es hier auch schon.
Das ist absolut korrekt, vielen Dank!

Nun haben, wenn ich das richtig verstehe, schon mehrere Leute in diesem Thread behauptet, daß die "neue" Art, gleichnamige Parent-Methoden über "super()" aufzurufen, nur dann korrekt funktionieren soll, wenn alle -- ALLE! -- beteiligten Methoden ihre Parents über "super()" aufrufen, während ich das immer noch bezweifle. Jedenfalls konnte ich für diese Behauptung keine Belege finden, und auch meine (bisherigen) Versuche deuten darauf hin, daß diese Behauptung falsch ist. Ganz im Gegenteil scheint es egal zu sein, ob ich die gleichnamige Methode der Parentklasse mit "super().<methode>()" oder "<parent>.<methode>(self)" aufrufe: der Effekt ist haargenau derselbe, mit Ausnahme etwas... subtiler Faktoren. Auch mitten in der Vererbungskette ist es anscheinend egal, ob ich die Parentmethode implizit über "super()" oder explizit über die Parent-Klasse aufrufe. Das sagen nicht nur die "trace"- und das "profile"-Module von Python selbst, sondern auch externe Programme wie strace(1) und ltrace(1).

Insofern möchte ich noch einmal darum bitten, daß einer der Teilnehmer mir einen Beleg für die Behauptung liefern möge, daß "super()" unter Python3 nur dann funktioniert, wenn alle Klassen in der Vererbungshierarchie "super()" aufrufen. Oder daß Old-Style-Classes nicht richtig funktionieren. Oder daß Old-Style-Classes nicht richtig mit "super()" funktionieren. Oder warum Tkinter/Tix nicht mit Objektorientierung funktioniert. Oder daß die offizielle Python-Dokumentation zu Tkinter -- die, wohlgemerkt: "super()" benutzt und zeigt -- falsch ist. Herzlichen Dank für Eure Bemühungen.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@LukeNukem: „Old style“ Klassen gibt es in Python 3 nicht mehr, ich denke da muss man sich nicht mehr mit beschäftigen. Das es Probleme geben kann, wenn man `super()` nicht konsequent in der gesamten Hierarchie verwendet, wurde bereits gezeigt, Du willst das anscheinend nicht wahrhaben.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

__blackjack__ hat geschrieben: Donnerstag 23. September 2021, 10:08Das es Probleme geben kann, wenn man `super()` nicht konsequent in der gesamten Hierarchie verwendet, wurde bereits gezeigt, Du willst das anscheinend nicht wahrhaben.
Was wurde denn da genau gezeigt, und wo? Unstreitig ist, daß eine abgeleitete Klasse (womöglich) eine oder mehrere Methode(n) ihrer Elternklasse(n) aufrufen muß. Es macht dabei aber nach allen meinen Erfahrungen und Erkenntnissen keinen Unterschied, ob diese explizit über den Namen der Elternklasse oder implizit über den Proxy aufgerufen wird, den super() zurückgibt. Bislang habe ich noch keinen Beleg dafür gesehen, daß diese Aussage inkorrekt wäre, und der von Dir gezeigte "Beleg" für Deine Behauptung belegt ja nur das dokumentierte und bekannte Verhalten von super() bei gleichnamigen Methoden im Falle von Mehrfachvererbung. Ich finde auch in den mir zugänglichen Dokumentationen von super() keinen Beleg für Deine Behauptung -- und die Verwendung von super() in der (gemeinhin korrekten) offiziellen Dokumentation von Tkinter weist ja ebenfalls eher darauf hin, daß Deine Behauptung leider nicht zutrifft.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@LukeNukem: kbr hat das zum Beispiel gezeigt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

__blackjack__ hat geschrieben: Sonntag 26. September 2021, 15:38 @LukeNukem: kbr hat das zum Beispiel gezeigt.
kbr hat dankenswerterweise gezeigt, daß die Parent-Methode nicht aufgerufen wird, wenn nicht ENTWEDER super() (mit seinen spezifischen Eigenschaften) ODER die Methode der Elternklasse aufgerufen wird. Darum ging es aber gar nicht, wenn die Tkinter-Klassen rufen ja die Methode der Elternklassen auf, wo dies notwendig ist -- aber eben nicht mit super(), sondern explizit über die Parentklassen, wie das früher (vor super()) eben so üblich war. Insofern belegt das Beispiel von kbr, was vorher schon alle wußten und was ich natürlich auch gar nicht bestreite: wenn der Entwickler eine Methode nicht aufruft, dann wird sie nicht ausgeführt. Wer hätte das gedacht! ;-)

Um das noch einmal klarzustellen: ich bezweifle, daß ALLE Klassen super() benutzen müssen, wenn EINE Klasse super() benutzt. Ich bezweifle NICHT, daß alle Klassen einer Vererbungshierarchie womöglich gleichnamige Methoden ihrer Parentklassen aufrufen müssen. UND ich behaupte, daß es vollkommen egal ist, ob die Klassen nun super() oder einen expliziten Aufruf der Methode über die Parentklasse benutzen -- eben so, wie das früher üblich war und wie dann eben auch Tkinter das macht. Oder, anders gesagt: ich sage, daß es gleichgültig ist, ob "super().methode()" oder "Parent.methode(self)" benutzt wird, solange die Methode eben aufgerufen wird. Oder, noch anders gesagt: solange der Methodenaufruf erfolgt, ist es gleichgültig, wie er gemacht wird.

Deswegen ist es auch völlig in Ordnung, Tkinter mit super() zu benutzen, denn die Tkinter-Klassen rufen ihre Elternmethoden ja auf -- wenngleich nicht mit super(), sondern explizit über die Parentklasse. Genau diese meine Aussage und anhand meines Beispiels für Tkinter mit OO hast unter anderem Du allerdings bestritten, wenn ich Euch richtig verstanden habe. Jetzt bitte ich um Belege für Eure Aussage, aber bisher habt Ihr lediglich andere Dinge belegt, die ohnehin selbstverständlich waren und daher auch niemanden verwundern können. ;-)

Insofern, auf die Gefahr hin, mich zu wiederholen: würde bitte jemand ein Codebeispiel zeigen, warum alle Klassen einer Vererbungshierarchie super() benutzen müssen und wo, wie und warum es zu Fehlern führen soll, wenn eine oder mehrere Klassen in der Vererbungskette NICHT super() benutzen, sondern die Methoden der Elternklassen explizit über diese Elternklassen aufrufen, so wie Tkinter es tut. Meine Güte, Ihr seid drei Leute, die meinen objektorientierten Tkinter-Code deswegen kritisiert habt. Da kann es doch wohl nicht allzu schwierig sein, Eure Behauptungen "entweder alle benutzen super()" und "der Aufruf der Elternmethoden über die explizite Angabe der Parentklassen darf nicht mit super() gemischt werden" anhand von Code zu belegen? Und zwar bitte! anhand von Code, der genau diese Behauptungen belegt, keinen Unfug mit bekannten und dokumentierten Sachverhalten im Zusammenhang mit Mehrfachvererbung oder dem grundsätzlichen Aufruf von Methoden. Herzlichen Dank für Eure Bemühungen -- und natürlich viel Erfolg dabei.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

To whom it may concern: In keiner der tkinter Basis-Klassen, die seit Python 3 implizit von object erben, wird in "__init__()" "object.__init__(*args, **kwargs)" aufgerufen und die abgeleiteten Klassen reichen "überschüssige" Argumente auch nicht durch. Deshalb funktioniert super() in von tkinter abgeleiteten Klassen allenfalls zufällig, wenn in diesen abgeleiteten Klassen gleichfalls Mehrfachvererbung vorgenommen wird. Daher auch mein früherer Hinweis: tkinter ist nicht für die Verwendung mit super() entworfen.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

kbr hat geschrieben: Sonntag 26. September 2021, 20:07 To whom it may concern: In keiner der tkinter Basis-Klassen, die seit Python 3 implizit von object erben, wird in "__init__()" "object.__init__(*args, **kwargs)" aufgerufen und die abgeleiteten Klassen reichen "überschüssige" Argumente auch nicht durch. Deshalb funktioniert super() in von tkinter abgeleiteten Klassen allenfalls zufällig, wenn in diesen abgeleiteten Klassen gleichfalls Mehrfachvererbung vorgenommen wird. Daher auch mein früherer Hinweis: tkinter ist nicht für die Verwendung mit super() entworfen.
Wo steht denn, daß object.__init__() aufgerufen werden muß? Davon sehe ich in der Dokumentation von Python irgendwie... nichts. Und ich habe in den letzten Jahren eine ganze Reihe von Vererbungshierarchien mit Python entwickelt, mit und ohne Mehrfachvererbung, mit und ohne Tkinter, und alle funktionieren tadellos auch mit Basisklassen, die weder object.__init__() noch super().__init__() aufrufen. Aber sei's drum, das ist ein anderes Thema, zurück zur eigentlichen Frage.

Wo steht, daß object.__init__() unter Tkinter nicht aufgerufen wird? Ein kurzer Blick in den Code beweist eindeutig das Gegenteil dieser Behauptung, und darum zeigen die Tkinter-Entwickler, wie ich oben verlinkt habe, in ihrer offiziellen Dokumentation auch die Nutzung des super()-Builtin. Richtig ist nämlich: die Klassen von Tkinter erben meistens von tkinter.Widget, einer leeren Klasse, die wie folgt deklariert ist:

Code: Alles auswählen

class Widget: pass
(Quelle: tkinter/__init__.py für Python 3.9 unter Kubuntu 20.04 LTS ab Zeile 2590, hier verkürzt dargestellt.)

Das heißt: Widget erbt implizit ohnehin von object (Python 3, ne?) und damit natürlich auch dessen Konstruktor object.__init__(). Zumindest, solange wir uns einig sind, daß eine Kindklasse die Eigenschaften und Methoden ihrer Eltern erbt, wenn die Kindklasse diese nicht überschreibt, oder? Also erbt Widget implizit von object, wie Du, lieber kbr, es ja selbst absolut richtig sagst, und damit erbt Widget eben auch den Konstruktor __init__() von, na? Ja, haargenau: object! Ist das nicht super()? ;-) Und das könnte jeder, der Zugriff auf eine aktuelle Python-Installation mit Tkinter und ein bisschen Ahnung von Pythons Objektorientierung hat, in Sekunden herausfinden.

In diesem Beitrag [1] bemängelt der geschätzte Benutzer "__deets__", daß die Tkinter-Klasse "Button" nicht super().__init__() aufrufen würde, und Du, lieber kbr, schlägst im Prinzip jetzt in dieselbe Kerbe. TATSÄCHLICH erbt die Button-Klasse (selbe Datei wie oben, ab Zeile 2628) aber von? Genau: Widget, und der Konstruktor von Button ruft in Zeile 2650 genau was auf? Richtig, ich zitiere: "Widget.__init__(self, master, 'button', cnf, kw)". Überraschung, dadurch wird automatisch natürlich der Konstruktor von object, also object.__init__() aufgerufen, denn Widget hat ja keinen eigenen eigenen Konstruktor und erbt automatisch den von object. Und ja, natürlich werden dabei auch die Argumente korrekt übergeben! Und jetzt der Clou: da die meisten Klassen von Tkinter von Widget erben, funktioniert das auch überall, und zwar genau so, wie es die Entwickler von Tkinter ja dann auch in ihrer offiziellen Dokumentation beschreiben und zeigen. Trotzdem behauptet der Benutzer "Sirius3" sogar, die Dokumentation sei falsch: [2]! Also der Herr "Sirius3" glaubt tatsächlich sogar, es besser zu wissen als die Entwickler des Tkinter-Pakets... lustig, aber... nein.

An diesen Sachverhalten ändert sich auch dadurch nichts, daß die Teilnehmer hier es trotz meiner wiederholten Fragen nicht geschafft haben, meine Aussagen zu widerlegen und Code zu zeigen, der ihre Behauptungen belegen würde. Stattdessen kapriziert sich der Code, den die Herren hier als "Beweis" gezeigt haben, auf gänzlich andere Themen, nämlich einmal auf die MRO bei der Mehrfachvererbung und der korrekt dokumentierten Funktionalität von super(), und ein anderes Mal auf einen fehlenden bzw. auskommentierten Methodenaufruf, was aber beides gar nicht das Thema war.

Insofern, lieber kbr, so leid es mir tut: Dein Hinweis ist leider, schlicht und ergreifend: falsch, falsch, und nochmal falsch.

Richtig ist: Tkinter funktioniert wunderbar mit super(), so wie es die Tkinter-Entwickler dokumentiert haben, auch wenn Tkinter selbst intern kein super() benutzt, sondern explizite Aufrufe über die Basisklasse. Und dieser Hinweis von insgesamt -- ich habe nochmal nachgelesen -- vier Teilnehmern dieses Threads ist und bleibt falsch, bis es einer der Herren schafft, ein Beispiel in Form von Code zu zeigen. Bis dahin gilt: die Dokumentation und ich lagen und liegen richtig, und die Herren kbr, __blackjack__, __deets__ und Sirius3 lagen und liegen falsch. ;-)


[1] viewtopic.php?f=1&t=52445&start=15#p393141
[2] viewtopic.php?f=1&t=52445&start=15#p393144
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@LukeNukem: `Button.__init__()` ruft in der Tat `Widget.__init__()` auf und `Widget` *hat* eine `__init__()`-Implementierung, die *nicht* `object.__init__()` aufruft. Der wird da also nicht automatisch aufgerufen. Keine Ahnung wie Du darauf jetzt kommst. Würde auch überhaupt gar keinen Sinn machen `object.__init__()` mit den Argumenten aufzurufen die `Button` da übergibt.

Und natürlich sind MRO und Mehrfachvererbung Thema. Genau darum geht es bei `super()`, das ist die Aufgabe von `super()` das richtig zu machen, und genau das funktioniert nicht mehr richtig wenn das nicht alle Methoden in der Hierarchie machen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

__blackjack__ hat geschrieben: Montag 27. September 2021, 00:28 `Widget` *hat* eine `__init__()`-Implementierung,
Wannwowie soll das bitte so sein? Hast Du in den Code geschaut?

Aus "git blame tkinter/__init__.py":

Code: Alles auswählen

368e06b6f06 Lib/lib-tk/Tkinter.py   (Guido van Rossum     1997-11-07 20:38:49 +0000 2618) class Widget(BaseWidget, Pack, Place, Grid):
06d28153504 Lib/lib-tk/Tkinter.py   (Fredrik Lundh        2000-08-09 18:03:12 +0000 2619)     """Internal class.
5917ecb0a4f Lib/lib-tk/Tkinter.py   (Guido van Rossum     2000-06-29 16:30:50 +0000 2620) 
06d28153504 Lib/lib-tk/Tkinter.py   (Fredrik Lundh        2000-08-09 18:03:12 +0000 2621)     Base class for a widget which can be positioned with the geometry managers
06d28153504 Lib/lib-tk/Tkinter.py   (Fredrik Lundh        2000-08-09 18:03:12 +0000 2622)     Pack, Place or Grid."""
06d28153504 Lib/lib-tk/Tkinter.py   (Fredrik Lundh        2000-08-09 18:03:12 +0000 2623)     pass
Scheint also schon etwas länger so zu sein, daß Widget KEINE __init__()-Implementierung hat. Sind ja nur über zwanzig Jahre.

Wie dem auch sei, Ihr könnt ja glauben, was Ihr wollt, das mache ich meinerseits ja auch. Ich glaube, und begründe das mit meiner Erfahrung, der offiziellen Python-Dokumentation, und den Unittests von Tkinter, daß Tkinter wunderbar und fehlerfrei mit super() mit und ohne Parameter sowie natürlich auch mit <Elternklasse>.<methode>(self) funktioniert, zudem mit und ohne Mehrfachvererbung, wenn man deren dokumentierte Eigenheiten beachtet.

Eure Behauptungen, daß jede Klasse in einer Vererbungshierarchie super() benutzen müsse, wenn eine Klasse der Hierarchie super() benutzt, halte ich aus Erfahrung und auch aus offensichtlichen Kompatibilitätsgründen für falsch. Eure Behauptungen, daß im Zusammenhang mit super() auch immer object.__init__() aufgerufen werden müsse, halte ich aus denselben Gründen für falsch. Eure Behauptungen, daß super().<methode>() nicht dasselbe täte wie <Elternklasse>.<methode>(self), halte ich ebenfalls für falsch.

Alle diese Eure Behauptungen werde ich so lange für falsch halten, bis mir jemand einen überzeugenden Beleg für Eure Behauptungen liefert, wahlweise als Verweis auf eine offizielle Dokumentation oder eben als laufähigen Code, der die angeblichen Probleme demonstriert, von denen Ihr hier raunt. Bis dahin wünsche ich allenthalben einen schönen Abend und Happy Hacking allerseits. ;-)
Antworten