Programmierstil

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.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Okay, leuchtet mir nun ein. Danke für die Erklärung. :)
Benutzeravatar
Perlchamp
User
Beiträge: 172
Registriert: Samstag 15. April 2017, 17:58

@ alle:
es ist schwierig für einen Anfänger OHNE Weitblick da komplett durchzusteigen, zumal ich gerade erst dabei bin, mich mit Klassen auseinanderzusetzen. Was ich (hoffentlich) verstanden habe ist, dass man beispielsweise eine bestehende (build-in-)Funktion/Methode überschreiben, sprich (nach seinen Vorstellungen oder nach skriptbedingter Notwendigkeit) modifizieren kann, ohne die Ürsprungs-Methode/Funktion zu verändern. Praktisch (nicht faktisch !) gleichzusetzen mit einem Objekt (z.B. einer Liste), an das man eine Botschaft sendet, dessen Ergebnis/Auswertung man dann weiter verwertet. Das Original-Objekt bleibt dabei unangetastet/unverändert. Ich weiß, dass dieser Vergleich *hinkt*, aber so ist das nunmal mit Anfängern ...
Die Falle, in die ich (durch meine Argumentation und mein Anfängerwissen) getappt bin, war, dass ich glaubte,

Code: Alles auswählen

tk.Frame.__init__(self, master)
könne man mit

Code: Alles auswählen

def __init__(self, master=None)
gleichsetzen. Dabei ist

Code: Alles auswählen

def __init__(self, master=None)
im Grunde genommen eine Über*ladung* des Zuweisungs*operator*s '=', der verwendet wurde, um eine Instanz einer Klasse zu gernerieren/modifizieren, während

Code: Alles auswählen

tk.Frame.__init__(self, master)
eine Über*schreibung* ist, danke nochmals für die Aufklärung !
wer lesen kann ist klar im Vorteil ;-)
es gibt keine Probleme, sondern nur Lösungen !
Bildung ist die Freude auf mich selbst !
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Es ist spät, aber das klnigt nicht korrekt.

Dieser Vergleich mit der Liste hinkt. Listen beinhalten Elemente und haben nichts mit Botschaften zu tun.

Dann verwendest du das Wort "gleichsetzen" und sprichst im nächsten Moment vom "Überladen".
Nein.

tk.Frame.__init__(self, master) -> Ruft die Funktion __init__ der Klasse tk.Frame auf und übergibt die Parameter self und master.
def __init__(self, master=None) -> Ist der Funktionskopf der Funktion "__init__" und definiert die zu übergebenen Parameter "self" und "master", wobei der Default-Wert von "master" None ist, sollte er nicht übergeben werden.

snafu hat doch schon geschrieben, was passiert:
snafu hat geschrieben: Mittwoch 6. März 2019, 23:55Was man im vorliegenden Code sieht, ist das Überschreiben einer Methode durch die erbende Klasse und der Aufruf der gleichnamigen Methode aus der Elternklasse. Der Aufruf ist nötig, da Python sonst nur den Code aus der neuen "Version" von __init__() ausführen würde und somit alles aus der alten __init__() ignorieren würde.
Benutzeravatar
Perlchamp
User
Beiträge: 172
Registriert: Samstag 15. April 2017, 17:58

@sparrow :
ich geh pennen, gute nacht
wer lesen kann ist klar im Vorteil ;-)
es gibt keine Probleme, sondern nur Lösungen !
Bildung ist die Freude auf mich selbst !
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Ich glaube, du hast dich daran festgebissen, dass du jetzt in allem, was vorne und hinten zwei Unterstriche hat, einen überladenen Operator siehst. Dem ist nicht so. Sie kennzeichnen Funktionen, die in bestimmten Situationen vom Kompiler aufgerufen werden. __init__ ist, was vom Compiler bei der Instanzierung einer Klasse aufgerufen wird. Der Konstruktor zur Initialisierung.

Wenn du die offizielle Dokunentation durcharbeitest, kommst du irgendwann hier vorbei. Da sieht man, dass es für Objekte einige dieser Funktionen gibt.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Perlchamp hat geschrieben: Sonntag 10. März 2019, 01:11 Dabei ist

Code: Alles auswählen

def __init__(self, master=None)
im Grunde genommen eine Über*ladung* des Zuweisungs*operator*s '=', der verwendet wurde, um eine Instanz einer Klasse zu gernerieren/modifizieren, während

Code: Alles auswählen

tk.Frame.__init__(self, master)
eine Über*schreibung* ist, danke nochmals für die Aufklärung !
So wurde das nicht erklärt und so geht es auch leider am Thema vorbei. Den Zuweisungsoperator kann man nämlich gar nicht überladen. Man kann nur den == Operator überladen mittels __eq__() (equal / gleich), der aber halt für das Testen auf Gleichheit und nicht für Zuweisungen steht.

__init__() enthält Anweisungen zur Initialisierung eines neu erstellten Objekts. Wenn die Klasse Foo durch den Aufruf Foo() instanziiert wurde, dann wird automatisch __init__() aufgerufen (falls vorhanden) und darin werden dann üblicherweise irgendwelche Startwerte für das Exemplar festgelegt. Stell dir eine Maschine vor, die du anschaltest und für die Benutzung einrichtest - aber die noch kein Material bearbeitet hat. In diesen Startzustand sollte __init__() ein Exemplar versetzen.

Wenn du nun eine Maschine hast, die klassische Paprika-Chips, sowie extra-scharfe Chili-Chips herstellen soll, dann muss sie jeweils unterschiedlich eingerichtet werden. Dennoch wird sehr vieles gleich ablaufen. Also gibst du denen eigene __init__()-Methoden zur Spezialisierung. Darin musst du aber trotzdem explizit sagen, dass zusätzlich (hier sinnvollerweise am Anfang) der Ablauf / Code aus der ursprünglichen __init__()-Methode ausgeführt werden soll. Und das ist quasi dein tk.Frame.__init__(self, master).

Die eigentliche Überschreibung ist übrigens def __init__(self, master=None) und möglicherweise hat dich der Einwand von __blackjack__ hier etwas verwirrt bzw auch ich als ich Verdeckung eingebracht hatte. In Python haben Objekte immer genau die Methoden, die zuletzt definiert wurden und bereits vorhandene Methoden werden ggf im Kontext der abgeleiteten Klasse überschrieben, sind aber trotzdem über die Elternklasse aufrufbar. Bei statisch typisierten Sprachen lässt sich im Gegensatz zum dynamisch typisierten Python noch explizit festlegen, in welchem Kontext ein Objekt behandelt werden soll und dadurch erhält man unterschiedliche Schnittstellen. Und da kann man manchmal noch zusätzlich sagen, was im Falle von veränderten Methoden passieren soll. Das führt hier aber IMHO zu weit für einen Anfänger.
Benutzeravatar
Perlchamp
User
Beiträge: 172
Registriert: Samstag 15. April 2017, 17:58

@alle:

Code: Alles auswählen

class Application(tk.Frame):              #1
    def __init__(self, master=None):      #2
        tk.Frame.__init__(self, master)   #3
Warum hatte ich diese Frage eingentlich gestellt?
#1 die Klasse 'Application' erbt die Methoden/Funktionen von der Klasse *Frame* des Tkinter-Moduls.(OK)
#2 dies ist der Konstruktor, mit Hilfe dessen man ein neues Objekt der Klasse 'Application' instanziieren kann.(OK)
#3 da #1, so dachte ich die ganze Zeit, warum dann nochmals explizit 'erwähnen/festhalten', dass *Frame* die
ursprüngliche Klassse ist? Aber dies ist ja jetzt geklärt:
[...]Darin musst du aber trotzdem explizit sagen, dass zusätzlich (hier sinnvollerweise am Anfang) der Ablauf / Code aus
der ursprünglichen __init__()-Methode ausgeführt werden soll.[...]
Deswegen dachte ich, dass mittels der Klasse 'Application' bei der Instanziierung eines neuen Objektes *Frame* überladen
wird, um das neue Objekt dadurch zu spezialisieren. Dem ist aber, wenn ich das JETZT richtig verstanden habe, nicht so.
Danke dafür an alle !

@sparrow:
[...] dass du jetzt in allem, was vorne und hinten zwei Unterstriche hat, einen überladenen Operator siehst.
nein, dem ist definitiv nicht so! Aber beim Lernen hatte ich aus einem Buch folgendes 'mitgenommen':

Code: Alles auswählen

class Geld(object):
    .
    .
    def __init__(self, waehrung, betrag):
        self.waehrung = waehrung
        self.betrag = float(betrag)
    .
    .
Zum Schluß sei noch darauf hingewiesen, dass auch die __init__-Methode im Grunde eine Überladung ist, nämlich eine Überladung des Zuweisungsoperators '=', der verwendet wird, um eine Instanz einer Klasse zu generieren.
@snafu:
[...] Das führt hier aber IMHO zu weit für einen Anfänger.
Wie wahr !
wer lesen kann ist klar im Vorteil ;-)
es gibt keine Probleme, sondern nur Lösungen !
Bildung ist die Freude auf mich selbst !
Sirius3
User
Beiträge: 17745
Registriert: Sonntag 21. Oktober 2012, 17:20

@Perlchamp: woher hast Du denn das `Zum Schluß...`-Zitat her? Das ist ja totaler Quatsch. Man kann Klassen auch Erzeugen, ohne die mit = irgendwie zuzuweisen.
Tholo
User
Beiträge: 177
Registriert: Sonntag 7. Januar 2018, 20:36

Der Code kommt mir bekannt vor. Ich vermute Python3 von Michael Weigend Kapitel 10
Benutzeravatar
Perlchamp
User
Beiträge: 172
Registriert: Samstag 15. April 2017, 17:58

@ Sirius 3:
Python 3 - Lernen und professionell anwenden, 5.Auflage, Seite 291 von Michael Weigend
In der 7.Auflage steht's auch (noch) drin.
wer lesen kann ist klar im Vorteil ;-)
es gibt keine Probleme, sondern nur Lösungen !
Bildung ist die Freude auf mich selbst !
Benutzeravatar
Perlchamp
User
Beiträge: 172
Registriert: Samstag 15. April 2017, 17:58

... wenn wir gerade dabei sind:
in der Dokumentation der Mexican Tech über Tkinter steht drin (übersetzt):
Verschiedene Längen, Breiten und andere Abmessungen von Widgets können in vielen verschiedenen Einheiten beschrieben werden.[...] Sie können Einheiten angeben, indem Sie eine Bemaßung auf eine Zeichenfolge setzen, die eine Zahl enthält, gefolgt von :
c ... Zentimeter
i ... Zoll
m ... Millimeter
p ... Druckerpunkte (etwa 1/72 ″)
ich bekomme jedoch immer einen Error, u.a. dass eine Ganzzahl erwartet wird. Ich habe das bisher damit begründet, dass diese Doku für Python 2.7 geschrieben wurde. Im Buch von Michael Weigend steht's jedoch so ähnlich. Kann mir diesbezüglich jemand weiterhelfen ?
Wurde das in Python 3.7 abgeschafft ?

Code: Alles auswählen

# Breite gleich Anzahl der Zeichen
self.quit_button = tk.Button(self, text='Beenden', command=self.quit, width=20)
# Breite gleich Maßeinheit (cm)
self.quit_button = tk.Button(self, text='Beenden', command=self.quit, width='20c')
##TclError: expected integer but got "20c"
self.quit_button = tk.Button(self, text='Beenden', command=self.quit, width=(20, 'c'))
##TclError: expected integer but got "20 c"
self.quit_button = tk.Button(self, text='Beenden', command=self.quit, width=(20, c))
##NameError: name 'c' is not defined
self.quit_button = tk.Button(self, text='Beenden', command=self.quit, width=20c)
##invalid syntax
wer lesen kann ist klar im Vorteil ;-)
es gibt keine Probleme, sondern nur Lösungen !
Bildung ist die Freude auf mich selbst !
Sirius3
User
Beiträge: 17745
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Breite wird auch in Zeichen angegeben, da macht eine Längenangabe keinen Sinn. Das geht natürlich nur wo Längenangaben erlaubt sind.

Code: Alles auswählen

self.quit_button.place(padx='3cm')
Benutzeravatar
Perlchamp
User
Beiträge: 172
Registriert: Samstag 15. April 2017, 17:58

@Sirius3:
... woher soll man das wissen ?
Ich werd' noch mal verrückt ...
Der nachfolgende Auszug (meine Übersetzung) aus der Tkinter-Doku impliziert meiner Meinung nach, dass beides immer funktioniert oder funktionieren sollte.
5.1. Maße
Verschiedene Längen, Breiten und andere Abmessungen von Widgets können in vielen verschiedenen Einheiten beschrieben werden.
  • Wenn Sie eine Bemaßung auf eine Ganzzahl setzen, wird davon ausgegangen, dass Sie die Anzahl der Zeichen (width) oder die Anzahl der Zeilen (height) repräsentiert, falls es sich um Text handelt. Handelt es sich dagegen um Bilder, dann repräsentiert eine Ganzzahl die Anzahl der Pixel (in Höhe bzw. Breite).
  • Sie können Einheiten angeben, indem Sie eine Bemaßung auf eine Zeichenfolge setzen, die eine Zahl enthält, gefolgt von [...]
Und ebenso könnte ich argumentieren: "padx oder ipadx werden in Pixel angegeben, da macht eine Maßeinheit bzw. Längenangabe keinen Sinn." Ich möchte damit jetzt auch keinen Streit vom Zaun brechen, ihr/du könnt ja nichts dafür, dass es so ist. Ihr wißt es eben - danke dafür !
Leuts, ich werde nochmal verrückt. Warum denn auf einmal *place* (anstelle von *grid*) und warum *cm* ?
Gut, es funktioniert ja, aber bedeutet beispielsweise *3cm* jetzt 3 Zentimeter PLUS 3 Millimeter ? (Ich hab's mit einem Lineal abgemessen, und es ist beides gleich.)
OK, Lösung gefunden: ipadx=''3cmmbtrtra'' funktioniert auch, d,h, nach ersten Buchstaben wird der String abgeschnitten bzw. der Rest wird nicht mehr berücksichtig => Sirius3, damit hast du ZU VIEL (das *m*) Code geschrieben ;-), eine Totsünde (Scherz) !
Nein, vielen lieben Dank für deine tolle Hilfestellung !
wer lesen kann ist klar im Vorteil ;-)
es gibt keine Probleme, sondern nur Lösungen !
Bildung ist die Freude auf mich selbst !
Sirius3
User
Beiträge: 17745
Registriert: Sonntag 21. Oktober 2012, 17:20

Da ist mir ein schwerer Fehler unterlaufen. Muß natürlich heißen:

Code: Alles auswählen

self.quit_button.pack(padx='3cm')
Und ja, der erste Buchstabe reicht, ist aber völlig unleserlich.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Perlchamp hat geschrieben: Dienstag 12. März 2019, 17:12
[...] dass du jetzt in allem, was vorne und hinten zwei Unterstriche hat, einen überladenen Operator siehst.
nein, dem ist definitiv nicht so! Aber beim Lernen hatte ich aus einem Buch folgendes 'mitgenommen':

Code: Alles auswählen

class Geld(object):
    .
    .
    def __init__(self, waehrung, betrag):
        self.waehrung = waehrung
        self.betrag = float(betrag)
    .
    .
Zum Schluß sei noch darauf hingewiesen, dass auch die __init__-Methode im Grunde eine Überladung ist, nämlich eine Überladung des Zuweisungsoperators '=', der verwendet wird, um eine Instanz einer Klasse zu generieren.
Das ist leider totaler Unsinn. Mit = bindet man einfach ein Objekt an einen Namen. Über diesen Namen ist das Objekt anschließend abrufbar. Andernfalls würde es quasi im Nirvana verschwinden (außer es wird als Parameter weitergegeben).

Diese Zuweisung kann nicht durch Überladung gesteuert werden. Man hat bloß die Kontrolle darüber, welches Objekt zurückgegeben wird. __init__() verändert lediglich das neu erstellte Exemplar einer Klasse, gibt also selbst nichts zurück. Die eigentliche Erstellung des Objekts findet intern statt und lässt sich via __new__() bei Bedarf modifizieren. Das ist aber nur sehr selten nötig und geht eigentlich über den Horizont des OOP-Basiswissens weit hinaus.

Übrigens: In Python entspricht die Kombination aus __new__() und __init__() wohl am ehesten dem, was man allgemein in objektorientierten Sprachen als Konstruktor bezeichnen würde. Beide Methoden sind optional, d.h. neue Objekte können auch ohne diese erzeugt werden. Der Ablauf ist: Rufe __new__() auf, falls vorhanden, sonst erfolgt alternativ ein Standardablauf, um ein neues Objekt zu erzeugen. Gib dieses Objekt dann an __init__(), falls vorhanden, zur Initialisierung. Liefere anschließend das neu erstellte Objekt zurück an den Aufrufer (den Programmierer).

Das ist auch der Grund, warum bei __init__() kein return am Ende genutzt wird. ;)
Sirius3
User
Beiträge: 17745
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Methoden sind nicht „optional”. Sie werden wie andere Methoden ganz normal vererbt, letztlich von `object`.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Perlchamp hat geschrieben: Dienstag 12. März 2019, 19:53... woher soll man das wissen ?
Es steht in der Dokumentation.
Raten hilft da wenig. Die Informationen sind ja da. Man muss sich nur auf die Suche machen.
Benutzeravatar
Perlchamp
User
Beiträge: 172
Registriert: Samstag 15. April 2017, 17:58

@ snafu:
der Satz stammt ja nicht von mir, sondern war ein *Zitat* aus einem Lehrbuch ...

@ sparrow:
ja, klar, danke ... ich lese wohl die falschen Bücher, wie mir scheint ... danke für den Link :-)

@ Sirius3:
Fehler sind menschlich, habe keine Probleme damit ...
Die Methoden sind nicht „optional”. Sie werden wie andere Methoden ganz normal vererbt, letztlich von `object`.
... auf was (das ich geschrieben habe) beziehst du dich da jetzt? Dass Methoden vererbt werden ist mir bewußt, somit sind Methoden nicht optional, sondern nur z.T. deren Argumente/Parameter/Optionen, was aber durch die Klasse bzw. deren Funktionen/Methoden (letztendlich) festgelegt und somit auch vererbt wird. Und dann gibt's da natürlich auch noch sog. Defaultwerte, die auch nicht optional sind, sondern dann 'zum Einsatz kommen', wenn man deren Werte nicht (neu) festlegt oder festlegen muss oder die Argumente/Parameter/Optionen und deren (neuen) Werte nicht explizit aufführt ...
wer lesen kann ist klar im Vorteil ;-)
es gibt keine Probleme, sondern nur Lösungen !
Bildung ist die Freude auf mich selbst !
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Sirius3 hat geschrieben: Dienstag 12. März 2019, 20:29 Die Methoden sind nicht „optional”. Sie werden wie andere Methoden ganz normal vererbt, letztlich von `object`.
War vielleicht missverständlich ausgedrückt. Ich wollte sagen, dass nicht jede Klasse __init__() selbst implementieren muss.
Benutzeravatar
Perlchamp
User
Beiträge: 172
Registriert: Samstag 15. April 2017, 17:58

@ snafu:
Stichwort *statische* Methoden ?
wer lesen kann ist klar im Vorteil ;-)
es gibt keine Probleme, sondern nur Lösungen !
Bildung ist die Freude auf mich selbst !
Antworten