Hallo Community,
ich habe ein Problem mit der Objektorientierung. Ich habe vier Klassen in vier Skripten:
Skript1: Klasse A
Skript2: Klasse B
Skript3: Klasse C
Skript4: Klasse D
Die Klassen A bis C sind Unterklassen der Klasse D (Variablen und Funktionen von D wurden vererbt). Nun möchte ich in meinem Main-Skript ein Objekt der Klasse D erstellen und somit die Funktionen der anderen drei Klassen abarbeiten lassen (dies ist programmtechnisch auch schon realisiert). Wenn ich nun das Main-Skript ausführe, dann erhalte ich folgenden Fehler:
ImportError: cannot import name KlasseD
Ich weiß jetzt bereits, dass dies an den Imports liegt, die wie folgt sind:
Skript1: from Skript4 import KlasseD
Skript2: from Skript4 import KlasseD
Skript3: from Skript4 import KlasseD
Skript4: from Skript1 import KlasseA
from Skript2 import KlasseB
from Skript3 import KlasseC
Main-Skript: from Skript4 import KlasseD
Da beißt sich natürlich die Katze in den Schwanz, allerdings meckert der Interpreter sofort, wenn ich die Imports so nicht mache. Ich möchte im Prinzip der Übersichtlichkeit wegen für jede Klasse ein eigenes Skript haben (so habe ich es auch mal mit den Header- und Source-Files in C und C++ gelernt).
Wie kann ich sowas in Python anwenden, oder macht man das hier nicht in der Art?
Objektorientierung und Vererbung mit mehreren Skripten
@AnyOne: Wenn `A`, `B`, und `C` Unterklassen von `D` sind, warum muss `Skript4` dann die Unterklassen importieren‽ Zirkuläre importe deuten in aller Regel auf einen ungünstigen Entwurf hin. Wenn sich zwei Module zwingend gegenseitig importieren müssen, ist der Inhalt so eng miteinenander verbunden, dass man die Module gar nicht einzeln verwenden kann, sich also auch die Frage stellt warum es dann auf zwei Module aufgeteilt wurde.
Eine Klasse pro Modul ist in Python unüblich, weil das in gewisser Weise das Modul als Organisationseinheit entwertet. Module sind dazu da thematisch zusammengehörige Klassen und Funktionen zu bündeln. Diese Aufgabe sollte man nicht auf Packages verlagern.
Was in anderen Programmiersprachen sinnvoll ist, sollte man nicht unbesehen auf andere Sprachen übertragen.
Bei C hat man keine Klassen, d.h. dort bündelt man thematisch zusammengehörige Funktionen in einer Übersetzungseinheit. Und bei C++ würde ich mal behaupten, dass man das macht, weil die Methoden in der Implementierungsdatei letztendlich auch wieder wie Funktionen einzeln aufgeführt werden, und in der Regel für die gleiche Semantik deutlich mehr Quelltextzeilen als in Python anfallen. So dass es dort tatsächlich etwas schwieriger ist in einer Datei mit Implementierungen für mehr als eine Klasse zu navigieren.
Eine Klasse pro Modul ist in Python unüblich, weil das in gewisser Weise das Modul als Organisationseinheit entwertet. Module sind dazu da thematisch zusammengehörige Klassen und Funktionen zu bündeln. Diese Aufgabe sollte man nicht auf Packages verlagern.
Was in anderen Programmiersprachen sinnvoll ist, sollte man nicht unbesehen auf andere Sprachen übertragen.
Bei C hat man keine Klassen, d.h. dort bündelt man thematisch zusammengehörige Funktionen in einer Übersetzungseinheit. Und bei C++ würde ich mal behaupten, dass man das macht, weil die Methoden in der Implementierungsdatei letztendlich auch wieder wie Funktionen einzeln aufgeführt werden, und in der Regel für die gleiche Semantik deutlich mehr Quelltextzeilen als in Python anfallen. So dass es dort tatsächlich etwas schwieriger ist in einer Datei mit Implementierungen für mehr als eine Klasse zu navigieren.
@BlackJack: Danke für die zügige Antwort 

Ehrlich gesagt habe ich mich auch bereits gewundert, warum ein objektorientiertes Programmieren über mehrere Skripte in der Literatur nicht groß beschrieben wird (ich habe u.a. die Kapitel im Galileo Openbook dazu gelesen).

Gut, das heißt also für mich, dass ich alle Klassen in ein Skript schreibe und dieses dann einfach in das Main-Skript importiere? Momentan sieht meine Projektstruktur wie folgt aus:Eine Klasse pro Modul ist in Python unüblich, weil das in gewisser Weise das Modul als Organisationseinheit entwertet. Module sind dazu da thematisch zusammengehörige Klassen und Funktionen zu bündeln. Diese Aufgabe sollte man nicht auf Packages verlagern.
- - Package
- - Main Skript
- Skript1
- Skript2
- Skript3
- Skript4
- - Main Skript
Da stimme ich dir zuWas in anderen Programmiersprachen sinnvoll ist, sollte man nicht unbesehen auf andere Sprachen übertragen.

Stimmt, in C gab es ja keine Klassen, sorry. In C++ konnte man so leichter arbeiten und hat meiner Meinung nach besser gesehen, wie die Zusammenhänge sind.Bei C hat man keine Klassen, d.h. dort bündelt man thematisch zusammengehörige Funktionen in einer Übersetzungseinheit. Und bei C++ würde ich mal behaupten, dass man das macht, weil die Methoden in der Implementierungsdatei letztendlich auch wieder wie Funktionen einzeln aufgeführt werden, und in der Regel für die gleiche Semantik deutlich mehr Quelltextzeilen als in Python anfallen. So dass es dort tatsächlich etwas schwieriger ist in einer Datei mit Implementierungen für mehr als eine Klasse zu navigieren.
Ehrlich gesagt habe ich mich auch bereits gewundert, warum ein objektorientiertes Programmieren über mehrere Skripte in der Literatur nicht groß beschrieben wird (ich habe u.a. die Kapitel im Galileo Openbook dazu gelesen).
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
Das Buch als gesamtes, aber insbesondere genau diese Kapitel sind besonders schlecht! Hier im Forum raten wir allen Anfängern dringend von diesem Buch ab. Einen Rant dazu findest Du hier.AnyOne hat geschrieben: Ehrlich gesagt habe ich mich auch bereits gewundert, warum ein objektorientiertes Programmieren über mehrere Skripte in der Literatur nicht groß beschrieben wird (ich habe u.a. die Kapitel im Galileo Openbook dazu gelesen).
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert
Gut, das konnte ich natürlich vorher nicht wissen - danke für die Info! Ich werde mir den Rant dazu einmal durchlesen. Als weitere Literatur habe ich noch "Programming Python" von Mark Lutz aus dem "O'Reilly Media" Verlag zur Verfügung. Ist dieses besser?Hyperion hat geschrieben:Das Buch als gesamtes, aber insbesondere genau diese Kapitel sind besonders schlecht! Hier im Forum raten wir allen Anfängern dringend von diesem Buch ab. Einen Rant dazu findest Du hier.AnyOne hat geschrieben: Ehrlich gesagt habe ich mich auch bereits gewundert, warum ein objektorientiertes Programmieren über mehrere Skripte in der Literatur nicht groß beschrieben wird (ich habe u.a. die Kapitel im Galileo Openbook dazu gelesen).
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
Schlechter geht ja kaumAnyOne hat geschrieben: Als weitere Literatur habe ich noch "Programming Python" von Mark Lutz aus dem "O'Reilly Media" Verlag zur Verfügung. Ist dieses besser?


encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert
Bliebe noch eine Frage.
Die Abhängigkeit darf eigentlich gar nicht auftreten.
Was hat Klasse D mit seinen Kindern zu schaffen?AnyOne hat geschrieben:Nun möchte ich in meinem Main-Skript ein Objekt der Klasse D erstellen und somit die Funktionen der anderen drei Klassen abarbeiten lassen
Die Abhängigkeit darf eigentlich gar nicht auftreten.
@AnyOne: Grundsätzlich bleibt bei mir immer noch die Frage warum `Skript4` die anderen Module überhaupt importieren muss? Wenn da wirklich nur jeweils die Klassen drin sind, warum muss die Basisklasse dann ihre Unterklassen kennen? Das klingt komisch.
Wenn man zirkuläre Abhängigkeiten bei Modulen hat, gibt es in der Regel zwei verschiedene Wege diese aufzulösen. Module zusammenfassen, oder den Teil der die Abhängigkeiten zu beiden Modulen hat, in ein eigenes Modul auslagern. Je nach dem was mehr Sinn macht.
Was sollte man grossartig zu objektorientierter Programmierung über mehrere Module (Skripte ist hier irgendwie der falsche Begriff) beschreiben. Neben den technischen Aspekten — Quelltextdatei beschreibt Modul, Verzeichnis mit `__init__`-Modul macht ein Package aus, absolute und relative Importe — bleibt doch eigentlich nur zu sagen, dass ein Modul thematisch zusammengehörende Klassen und Funktionen zusammenfasst und Packages thematisch zusammengehörende Module zusammenfassen. Wenn ein Modul zu lang wird, kann man es aufteilen. Wobei man dabei auf eine sinnvolle Teilung Wert legen sollte. Also nicht nach dem Muster man hat 10 Klassen und verschiebt die jetzt willkürlich 5:5 auf zwei Module.
Ich würde noch den Tipp geben mehrmodulige (gibt's das Wort?) Programme in ein Package zu stecken, damit man den obersten Namensraum sauber hält und nicht so darauf achten muss ob es schon andere Module mit den jeweiligen Namen gibt, mit denen man kollidieren könnte. Da man aus einem Modul ganz einfach ein Package machen kann, macht es nichts wenn man mit einem Modul startet und erst im Verlauf merkt, dass man mehr als eines braucht.
Wenn man zirkuläre Abhängigkeiten bei Modulen hat, gibt es in der Regel zwei verschiedene Wege diese aufzulösen. Module zusammenfassen, oder den Teil der die Abhängigkeiten zu beiden Modulen hat, in ein eigenes Modul auslagern. Je nach dem was mehr Sinn macht.
Was sollte man grossartig zu objektorientierter Programmierung über mehrere Module (Skripte ist hier irgendwie der falsche Begriff) beschreiben. Neben den technischen Aspekten — Quelltextdatei beschreibt Modul, Verzeichnis mit `__init__`-Modul macht ein Package aus, absolute und relative Importe — bleibt doch eigentlich nur zu sagen, dass ein Modul thematisch zusammengehörende Klassen und Funktionen zusammenfasst und Packages thematisch zusammengehörende Module zusammenfassen. Wenn ein Modul zu lang wird, kann man es aufteilen. Wobei man dabei auf eine sinnvolle Teilung Wert legen sollte. Also nicht nach dem Muster man hat 10 Klassen und verschiebt die jetzt willkürlich 5:5 auf zwei Module.
Ich würde noch den Tipp geben mehrmodulige (gibt's das Wort?) Programme in ein Package zu stecken, damit man den obersten Namensraum sauber hält und nicht so darauf achten muss ob es schon andere Module mit den jeweiligen Namen gibt, mit denen man kollidieren könnte. Da man aus einem Modul ganz einfach ein Package machen kann, macht es nichts wenn man mit einem Modul startet und erst im Verlauf merkt, dass man mehr als eines braucht.
Ich merke grad selbst wie schwachsinnig das eigentlich istSirius3 hat geschrieben:Bliebe noch eine Frage.Was hat Klasse D mit seinen Kindern zu schaffen?AnyOne hat geschrieben:Nun möchte ich in meinem Main-Skript ein Objekt der Klasse D erstellen und somit die Funktionen der anderen drei Klassen abarbeiten lassen
Die Abhängigkeit darf eigentlich gar nicht auftreten.

@Hyperion: Das ist ein englisches Buch

@BlackJack: Stimmt, das ist wirklich komisch. Danke für die Erklärung, so langsam wird mir klar wie es funktioniert
Irgendwie klappt das mit der OOP noch nicht so ganz -.-
Ich habe jetzt die Klassen alle in ein Modul geschrieben. Nun möchte ich eine "Elternklasse", in welcher vier Konstanten geschrieben werden. Auch soll diese eine Funktion beinhalten, welche alle anderen Klassen benutzen können. Dann würde diese Elternklasse ja wie folgt aussehen:
Nun steht direkt darunter die nächste Klasse (wenn ich das in den vorherigen Posts richtig verstanden habe):
Wäre der Aufruf so korrekt? Ich habe es nach diesem Muster nun in meinem Programm gemacht. Es wird der Konstruktor der Parent-Klasse aufgerufen, leider kommt das Programm dann nicht weiter. Was mache ich hier falsch?
Vielen Dank & viele Grüße
AnyOne
Ich habe jetzt die Klassen alle in ein Modul geschrieben. Nun möchte ich eine "Elternklasse", in welcher vier Konstanten geschrieben werden. Auch soll diese eine Funktion beinhalten, welche alle anderen Klassen benutzen können. Dann würde diese Elternklasse ja wie folgt aussehen:
Code: Alles auswählen
class Parent:
def __init__(self, A, B, C, D):
self.__A = A
self.__B = B
self.__C = C
self.__D = D
def getA(self):
return self.__A
def getB(self):
# ...
def Funktion(self, A, B):
# ...
Code: Alles auswählen
class Base(Parent):
def __init__(self, A, B, C, D, E)
Parent.__init__(self, A, B, C, D)
self.__E = E
self.__A = Parent.getA()
#...
Vielen Dank & viele Grüße
AnyOne
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
Vergiss erstmal dass es ``__`` überhaupt gibt, das macht alles kompliziert und nur begrenzt viel besser. Das ist kein Private-Mechanismus und wenn du das weglässt braucht du auch keine Getter und Setter schreiben.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Ok, also haben dann meine "Unterklassen" auf jeden Fall zugriff auf die Konstanten der Elternklasse? Sorry, zwei Jahre C und C++ hinterlassen ihre SpurenLeonidas hat geschrieben:Vergiss erstmal dass es ``__`` überhaupt gibt, das macht alles kompliziert und nur begrenzt viel besser. Das ist kein Private-Mechanismus und wenn du das weglässt braucht du auch keine Getter und Setter schreiben.

Du kannst den Zugriff nicht völlig zunageln. In Python gehen wir davon aus, dass es sich beim Entwickler um einen fachlich vorgebildeten Menschen handelt der weiß wann er nichts anfassen sollte, aber die Freiheit hat, gegebenenfalls doch einzugreifen wenn es nötig ist. In anderen Sprachen ist das mit der fachlichen Qualifikation wohl nicht ... ähmmm ... also was ich eigentlich sagen wollte war, dass in Python ein führender Unterstrich dazu verwendet wird um eine private Variable anzudeuten. Zwei führende Unterstriche verwursten dann noch den Namen der Klasse mit in das Attribut. Hier gilt: wenn du nicht weißt wofür es gut ist, dann brauchst du es nicht.AnyOne hat geschrieben:Ok, also haben dann meine "Unterklassen" auf jeden Fall zugriff auf die Konstanten der Elternklasse? Sorry, zwei Jahre C und C++ hinterlassen ihre Spuren

Den Effekt auf die interne Namensvergabe kannst du dir mit folgendem kleinen Code ansehen.
Code: Alles auswählen
class Foo(object):
def __init__(self):
self.a = None
self._b = None
self.__c = None
print(Foo().__dict__)
Noch ein paar Anmerkungen:
Alle Klassen werden üblicherweise von object abgeleitet.
Getter und Setter sind unüblich, statt dessen gibt es das Gegenteil: property
Elternfunktionen werden mit super aufgerufen:
Grüße
Sirius
Alle Klassen werden üblicherweise von object abgeleitet.
Getter und Setter sind unüblich, statt dessen gibt es das Gegenteil: property
Elternfunktionen werden mit super aufgerufen:
Code: Alles auswählen
class Parent(object):
def __init__(self, A, B, C, D):
self.A = A
self.B = B
self.C = C
self.D = D
def Funktion(self, A, B):
# ...
class Base(Parent):
def __init__(self, A, B, C, D, E)
super(Base,self).__init__(A, B, C, D)
self.E = E
Sirius
@AnyOne: Was mich jetzt noch ein wenig verwirrt ist der Begriff Konstante. Du gebrauchst den anscheinend anders als er üblicherweise verwendet wird, denn in Deinem Beispiel sind keine Konstanten zu sehen.
@/me: Danke für den Beispielcode, jetzt wird mir der Unterschied bewusst und das wird auch direkt geändert. Ich werde in Zukunft beim Programmieren von Python davon ausgehen, dass das Programm von Leuten verwendet wird, die nicht darin rumpfuschen 
@Siruis3: Das heißt also, wenn ich eine Klasse ableite und darin die "super" Funktion verwende, dann weiß der Interpreter direkt, dass damit die Elternklasse gemeint ist? Interessant das ich davon bisher leider nichts in der Literatur gefunden habe, scheint ja wirklich viel Käse auf dem Markt zu geben -.-
@BlackJack: Ich denke das trug wirklich zur Verwirrung bei! Ich wollte damit lediglich andeuten, dass diese Werte zu Beginn einmal vom Nutzer eingegeben werden sollen (es wird später noch eine GUI geben, welche das abfragt) und dann keine Veränderung in der Laufzeit mehr erfahren sollen. Deswegen - so lernte ich es eben im Studium mit C++ - wollte ich diese kapseln.
Ich danke euch allen nochmals sehr für eure Hilfsbereitschaft! Das ist wirklich ein tolles Forum!

@Siruis3: Das heißt also, wenn ich eine Klasse ableite und darin die "super" Funktion verwende, dann weiß der Interpreter direkt, dass damit die Elternklasse gemeint ist? Interessant das ich davon bisher leider nichts in der Literatur gefunden habe, scheint ja wirklich viel Käse auf dem Markt zu geben -.-
@BlackJack: Ich denke das trug wirklich zur Verwirrung bei! Ich wollte damit lediglich andeuten, dass diese Werte zu Beginn einmal vom Nutzer eingegeben werden sollen (es wird später noch eine GUI geben, welche das abfragt) und dann keine Veränderung in der Laufzeit mehr erfahren sollen. Deswegen - so lernte ich es eben im Studium mit C++ - wollte ich diese kapseln.
Ich danke euch allen nochmals sehr für eure Hilfsbereitschaft! Das ist wirklich ein tolles Forum!

Bevor man `super()` verwendet, sollte man es IMHO verstanden haben und welche Konsequenzen sich daraus ergeben: Python's Super is nifty, but you can't use it.
Ich bleibe bei einem einfachen ``Parent.__init__(A, B, C, D)`` und hatte bisher auch noch keinen Fall wo ich die ganze Komplexität von `super()` gebraucht hätte.
Ich bleibe bei einem einfachen ``Parent.__init__(A, B, C, D)`` und hatte bisher auch noch keinen Fall wo ich die ganze Komplexität von `super()` gebraucht hätte.
Gut, so habe ich es bisher auch immer verwendet und werde dann wohl auch dabei bleiben. Ich möchte mir nicht noch mehr Probleme einfangen..BlackJack hat geschrieben:Bevor man `super()` verwendet, sollte man es IMHO verstanden haben und welche Konsequenzen sich daraus ergeben: Python's Super is nifty, but you can't use it.
Ich bleibe bei einem einfachen ``Parent.__init__(A, B, C, D)`` und hatte bisher auch noch keinen Fall wo ich die ganze Komplexität von `super()` gebraucht hätte.
- pillmuncher
- User
- Beiträge: 1530
- Registriert: Samstag 21. März 2009, 22:59
- Wohnort: Pfaffenwinkel
@BlackJack: Raymond Hettinger findet super super.
In specifications, Murphy's Law supersedes Ohm's.
@pillmuncher: Ja den Artikel kenne ich, bis zu dem hatte ich Hettinger mindestens für einen Halbgott gehalten. Die Idee bei allen Methoden immer auch **kwargs entgegen nehmen zu müssen und alle Methoden fortan nur noch mit Schlüsselwort-Argumenten aufrufen zu müssen, finde ich so alles andere als super. Bei der Adapterklasse habe ich mich dann gefragt ob er auf Drogen ist.