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

@Perlchamp: __init__() ist wie __add__() eine sogenannte Dunder-Methode (wegen den beiden Unterstrichen) und beide haben eine spezielle Bedeutung. Das Überschreiben von __init__() steuert aber nicht das Verhalten von irgendeinem Operator. Welcher sollte das auch sein?

Ich schrieb übrigens explizit vom Überladen von Methoden / Funktionen, nicht von Operatoren. Wenn man beides in einem Satz nennt, ist wahrscheinlich die Terminologie noch nicht ganz klargeworden. Funktionen kommen in Mathe ja eigentlich spätestens in der Oberstufe dran und den Begriff Operator hat man dann vielleicht auch schonmal gehört... :)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Vielleicht nochmal, um die Verwirrung hoffentlich zu beseitigen: Über*laden* ist etwas anderes als Über*schreiben*. Und Überladen von *Methoden* ist etwas anderes als Überladen von *Operatoren*.

Für das Überladen von Operatoren hat man in Python spezielle Dunder-Methoden: __add__() für den Additionsoperator (+), __mul__() für das Mal-Zeichen (Multiplikation), usw. Denen darf man nach Belieben neues Verhalten zuweisen. So könnte man Plus und Minus vertauschen, wenn es einem Spaß macht. Oder aber - in ernsteren Zusammenhängen - das Größer-Als Zeichen z.B. für Umleitungen nutzen, ähnlich wie man das von der Shell kennt.

Überladen von Methoden heißt etwas ganz anderes. Nämlich dass anhand von Parameteranzahl und Parametertyp eine jeweils eigene Variante der Methode aufgerufen wird. Wenn man also foo(x) und foo(x, y) definiert hat, dann kennt der Compiler zwei foo's und ruft das jeweils passende foo() bei Übergabe von einem oder von zwei Parametern auf. Bei drei oder null Parametern gäbe es einen Fehler. Python unterstützt aber nur foo(x) oder foo(x, y). Beides zusammen geht nicht. Dann würde die zuletzt gemachte Definition die ältere überschreiben.

Und dann sind wir beim Thema Überschreiben. Das heißt einfach, dass die überschriebene Funktion oder Methode von der Neuen überdeckt wird. Im Falle von Vererbungen kommt man dennoch an die ursprüngliche Methode, wenn man sie über die Elternklasse aufruft. Das findet man häufig bei Vererbungen, dass man quasi das Original benutzen möchte, aber etwas hinzufügt (z.B. Loggen von Fehlern).

Und nein, du wusstest offenbar nicht, was ich meinte und hast mir Worte in den Mund gelegt, die einen ganz anderen Kontext haben, da du eben nicht den Zusammenhang verstanden hast. Ist hoffe aber, nun sind die Unterschiede klarer geworden. ;)
Benutzeravatar
__blackjack__
User
Beiträge: 13109
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wobei das Beispiel mit dem Überladen von `foo` ein bisschen unvollständig ist, denn das war ja nur mit der Anzahl der Argumente. Die muss für's überladen gar nicht unterschiedlich sein. Man kann auch die gleiche Anzahl, aber unterschiedliche Typen haben. Dafür bräuchte man dann aber Typdeklarationen und einen Compiler der je nach Typ die entsprechende Funktion wählt.

Wenn man sich beispielsweise mal `bytes()` anschaut, dass wäre etwas, was man in statisch typisierten Sprachen die Überladen unterstützen mit vier oder fünf verschiedenen überladenen Definitionen lösen würde. Ein Beispiel das nur mit der Anzahl der Argumente auskäme, wäre `range()`.

Damit Überschreiben mit Überdecken/Verdecken gleichzusetzen wäre ich vorsichtig. Da kommt man in Python mit durch, aber es gibt Sprachen die einen Unterschied machen, ob man eine Methode überschreibt oder überdeckt/vedeckt. Überschreiben braucht/erzeugt eine ”virtuelle” Methode und einige Sprachen erfordern dann auch das man explizit ein Schlüsselwort wie ``overriding`` verwendet um klar zu stellen das man überschreibt, und nicht verdeckt, was man ohne das Schlüsselwort tun würde. Das ist jetzt genau wie das Überladen in Python natürlich schlecht anschaulich zu erklären, weil in Python grundsätzlich alle Methoden ”virtuell” sind.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

__blackjack__ hat geschrieben: Freitag 8. März 2019, 10:12 Wobei das Beispiel mit dem Überladen von `foo` ein bisschen unvollständig ist
Ich bin beim Beispiel nicht weiter auf die Typunterscheidung eingegangen, da dies in Python nicht relevant ist. Genannt hatte ich den Punkt aber bereits.
__blackjack__ hat geschrieben: Freitag 8. März 2019, 10:12 Damit Überschreiben mit Überdecken/Verdecken gleichzusetzen wäre ich vorsichtig. Da kommt man in Python mit durch, aber es gibt Sprachen die einen Unterschied machen, ob man eine Methode überschreibt oder überdeckt/vedeckt.
Und das macht dann genau welchen Unterschied im Verhalten? Dient das nicht nur dafür, dem Compiler mitzuteilen, dass hier bereits eine gleichnamige Methode existiert, also dass er warnt wenn diese wider Erwarten doch nicht existiert (z.B. weil man sich vertippt hat) bzw umgekehrt nicht anmeckert, dass man einen bereits vergebenen Namen für die Methode gewählt hat? So gesehen wäre Überschreiben dann halt "bewusstes Überdecken"...
Benutzeravatar
__blackjack__
User
Beiträge: 13109
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@snafu: Die Frage ist welche Methode aufgerufen wird wenn man einen Typ als einen Basistyp behandelt. Also ob dann die überschriebene Methode des Typs aufgerufen wird, oder die verdeckte Methode.

Im folgenden C++-Beispiel wird `foo()` überdeckt und `bar()` überschrieben:

Code: Alles auswählen

#include <iostream>

using namespace std;

class Base
{
public:
    void foo() { cout << "Base foo" << endl; }
    virtual void bar() { cout << "Base bar" << endl; }
};

class Sub : public Base
{
public:
    void foo() { cout << "Sub foo" << endl; }
    virtual void bar() { cout << "Sub bar" << endl; }
};

int main()
{
    Sub sub;
    sub.foo();
    sub.bar();
    
    Base &base = sub;
    base.foo();
    base.bar();

    return 0;
}
Die Ausgabe davon ist:

Code: Alles auswählen

Sub foo
Sub bar
Base foo
Sub bar
Wenn man ein `Sub`-Objekt als `Base`-Objekt behandelt, wird trotzdem die in `Sub` überschriebene Methode aufgerufen. Bei der in `Sub` bloss verdeckten Methode wird aber die Implementierung in `Base` verwendet.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
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: 17750
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: 17750
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: 17750
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. ;)
Antworten