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

__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: 13061
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. ;-)
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@LukeNukem: Okay, ich habe mir nur die `Widget`-Klasse im Interpreter angeschaut und festgestellt das die entgegen Deiner Behauptung nicht von `object` ist. Sie kommt von `BaseWidget` und sie ruft keine weitere `__init__()` in der Hierarchie weiter oben auf, also auch nicht die von `object`.

Und das ist nicht nur unsere Behauptung. Der Superharmful-Artikel ist ja nun schon ein paar Jahre in der Welt, und auch Leute die super() super finden, wie Raymond Hettinger (Python-Core-Dev) behaupten das alles ohne das ich dagegen Widerspruch finden kann. Ich kann dagegen deren Argumente und Codebeispiele nachvollziehen. Aber na klar, Du brauchst das ja nicht glauben. Du kannst auch das halbfunktionierende `super()` nehmen und den nicht funktionierenden Teil mit „wenn man deren dokumentierte Eigenheiten beachtet“ umschreiben.
„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, 23:37 @LukeNukem: Okay, ich habe mir nur die `Widget`-Klasse im Interpreter angeschaut und festgestellt das die entgegen Deiner Behauptung nicht von `object` ist. Sie kommt von `BaseWidget` und sie ruft keine weitere `__init__()` in der Hierarchie weiter oben auf, also auch nicht die von `object`.
Stimmt, da ist mir etwas durchgegangen, vielen Dank für den Hinweis und die Klarstellung. Widget erbt von BaseWidget, Pack, Place und Grid, und BaseWidget erbt seinerseits von Misc. Pack, Place, Grid und Misc haben keine __init__()-Implementierung und erben in Python3 damit implizit von object -- und damit natürlich auch die __init__()-Methode von object. Allerdings, da hast Du Recht, wird object.__init__() nicht aufgerufen, weder explizit noch implizit.

Aber: muß object.__init__() tatsächlich aufgerufen werden, damit super() korrekt funktioniert? Man könnte James Y. Knights "Super-Harmful"-Artikel von 2007 zwar durchaus so verstehen, aber diese Behauptung sehe ich durch keine weitere Quelle außer diesem Artikel gedeckt. Die Standardbibliothek von Python macht das auch nicht, auch nicht in jüngeren Libraries wie asyncio. Siehe dazu zum Beispiel die Klasse Handle in Lib/asyncio/events.py, die object.__init__() weder direkt noch indirekt über super() aufruft -- aber dennoch kann TimerHandle (selbe Datei) offenbar davon erben und über super().__init__() die __init__()-Implementierung von Handle aufrufen.

Tatsächlich wird object.__init__ im aktuellen Python-Code auch ausschließlich in Tests aufgerufen -- ansonsten gibt es lediglich einige Prüfungen wie "if [...] or (cls.__init__ is object.__init__)" (Lib/_threading_local.py) und Zuweisungen "cls.__init__ = object.__init__" (Lib/typing.py). Anders gesagt: wenn James Y. Knight Recht hätte bzw. diese Interpretation seiner Aussagen zuträfe, dann wäre ein großer Teil von Pythons Standardbibliotheken kaputt und man könnte super() nicht benutzen, wenn man von Klassen aus Pythons Standardbibliothek erbt.

Ich weiß ja nicht, wie Deine Erfahrungen sind, aber nach meinen ist das nicht der Fall. Python funktioniert enorm stabil und zuverlässig auch mit Einfach- und Mehrfachvererbung -- sowie mit super(), wenn man dessen Eigenheiten beachtet. Deswegen halte ich eher die Aussage von Herrn Knight für entweder falsch oder falsch verstanden, als die Dokumentation und die Standardbibliotheken von Python.
__blackjack__ hat geschrieben: Montag 27. September 2021, 23:37 Und das ist nicht nur unsere Behauptung. Der Superharmful-Artikel ist ja nun schon ein paar Jahre in der Welt, und auch Leute die super() super finden, wie Raymond Hettinger (Python-Core-Dev) behaupten das alles ohne das ich dagegen Widerspruch finden kann. Ich kann dagegen deren Argumente und Codebeispiele nachvollziehen.
Raymond Hettinger, James Y. Knight und der RealPython-Arikel zu super() empfehlen in ihren Ausführungen, die Klassen kooperativ zu gestalten und im Zweifelsfall *args und **kwargs durchzureichen. Wenn Du Dir meinen Code unter [1] noch einmal genau anschaust, tut er: exakt das.

Richtig ist, daß es bei der Verwendung von super() im Zusammenhang mit Mehrfachvererbung auf die MRO und natürlich auf die entsprechende Behandlung von Argumenten ankommt. Das ist im Zusammenhang mit meinem Codebeispiel allerdings irrelevant, denn meine Klassen "Action" -- die implizit von object erbt -- und MainWin -- die lediglich von tkinter.Tk erbt -- benutzen _beide_ keine Mehrfachvererbung.

Daß die Elternklassen, von denen meine Klassen erben, ihrerseits Mehrfachvererbung nutzen und die Methoden ihrer Elternklassen explizit über die Namen der Elternklassen aufrufen, ist korrekt, aber IMHO irrelevant. Es spielt IMHO auch letztlich keine Rolle, ob meine MainWin-Klasse nun tk.Tk.__init__(self, *args, **kwargs) oder super().__init__(*args, **kwargs) aufruft, solange es nur aufgerufen wird.

Ansonsten führt James Y. Knights "Super-Harmful"-Artikel von 2007 einige Aussagen ins Feld, die ich ansonsten durch keine andere Quelle gedeckt sehe -- weder durch Code, noch durch Dokumentation, und auch nicht durch meine eigenen Erfahrungen. Stellenweise sind seine Formulierungen allerdings durchaus mehrdeutig.

[1] viewtopic.php?f=1&t=52445&start=15#p393123
__blackjack__ hat geschrieben: Montag 27. September 2021, 23:37 Aber na klar, Du brauchst das ja nicht glauben. Du kannst auch das halbfunktionierende `super()` nehmen und den nicht funktionierenden Teil mit „wenn man deren dokumentierte Eigenheiten beachtet“ umschreiben.
Nun, letzten Endes laufen Eure Behauptungen darauf hinaus, daß Klassen auch in der Standardbibliothek, die die Methoden ihrer Eltern explizit über den Namen der Elternklasse(n) aufrufen und / oder object.__init__() nicht aufrufen, nicht mit super() verwendet werden können. Das halte ich allerdings schon aus Gründen der Kompatibilität für eine zumindest sehr gewagte Behauptung, denn es würde ein heilloses Chaos produzieren und Vererbung im Allgemeinen und super() im Besonderen letztlich dermaßen unbenutzbar machen, das es komplett sinnlos wäre. Aber wie erwähnt sehe ich diese Aussage allerdings auch, mit Ausnahme des mehrdeutig formulierten Artikels von James Y. Knight, von keiner Quelle oder Erfahrung gedeckt.
Antworten