Interessantes import-Verhalten

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.
Antworten
Alfons
User
Beiträge: 7
Registriert: Montag 18. Februar 2019, 16:08

Ich habe folgende zwei Pythondateien im Verzeichnis Tester:

File 1: Tester/haupt.py
import sub
print("haupt.py")
def hauptFunc():
print("In haupt.py (hauptFunc)")
# sub.subFunc() => uncommenting results in error


File 2: Tester/sub.py
import haupt
print("sub.py")
def subFunc():
print("In subt.py (subFunc)")
# haupt.hauptFunc() => uncommenting results in error

Das Ausführen von File1 (haupt.py) führt zu folgender Ausgabe:
haupt.py
sub.py
haupt.py

Das Ausführen von File2 (sub.py) erzeugt entsprechend die Ausgaben:
sub.py
haupt.py
sub.py

Meine erste Frage: Wird bei einem Import immer automatisch die gesamte Datei ausgeführt oder wie kommen die drei Ausgaben zustande?
Meine zweite Frage: Wenn ich die beiden Kommentare wegnehme (aber den Rest der Zeile so belasse) erhalte ich jeweils folgende Fehlermeldung:
AttributeError: module 'sub' has no attribute 'subFunc'
AttributeError: module 'haupt' has no attribute 'hauptFunc'

Meine zweite Frage daher: Wieso erhalte ich diese Fehlermeldungen und wie lässt sich dies vermeiden?
Die Dateien wurden unter IDLE erstellt (Version 3.6.1)
Herzlichen Dank für jede Info.
Alfons
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Alfons: Zu Anfang gleich das wichtigste: Mach so etwas nicht! Wenn sich zwei Module gegenseitig benötigen, dann sind die offensichtlich so verbunden, das sie in ein Modul gehören und nicht in zwei. Oder das man gemeinsam benötigtes in ein drittes Modul auslagern will.

Module werden beim ersten Importieren ausgeführt. Anweisung für Anweisung. Wenn eine von diesen Anweisungen ein ``import`` ist, dann wird die Ausführung an der Stelle unterbrochen, ähnlich wie bei einem Funktionsaufruf, und das importierte Modul wird ausgeführt, sofern das der erste Import von dem Modul ist. Wenn es sich nicht um den ersten Import handelt, dann gibt es das Modulobjekt ja schon und das wird durch den ``import`` einfach eingebunden. Wenn man dabei ein Modul einbindet, das noch nicht komplett abgearbeitet wurde, gibt es die Attribute die noch nicht definiert wurden natürlich noch nicht. Eine Funktion existiert erst, nachdem dir entsprechende ``def``-Anweisung abgearbeitet wurde. Darum gibt es `subFunc()`/`hauptFunc()` noch nicht, zu den Zeitpunkten wo Du versuchst sie zu verwenden.

Namen schreibt man in Python übrigens klein_mit_unterstrichen. Ausnahmen: Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Nachtrag: Das Modul das als Programm ausgeführt wird, wird als Modul `__main__` ausgeführt. Wenn man das noch einmal importiert unter dem eigentlichen Modulnamen, dann existiert das zweimal! Also auch das sollte man nicht machen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
Perlchamp
User
Beiträge: 172
Registriert: Samstag 15. April 2017, 17:58

@ _blackjack_ :
hallo, ich hätte dazu auch noch eine Verständnisfrage :
Module werden beim ersten Importieren ausgeführt. Anweisung für Anweisung. Wenn eine von diesen Anweisungen ein ``import`` ist, dann wird die Ausführung an der Stelle unterbrochen, ähnlich wie bei einem Funktionsaufruf, und das importierte Modul wird ausgeführt, sofern das der erste Import von dem Modul ist.
wie kommen dann die Ausgaben (bei Ausführung von haupt.py) zustande ?
haupt.py
sub.py
haupt.py
Abarbeitung (Ausführung von haupt.py):
haupt.py: import sub (erster Import) => sub.py: import haupt (erster Import) => haupt.py: print("haupt.py")=> haupt.py: def hauptFunc() [erzeugt keine Ausgabe, da es eine Prozedur ist - kein return] => sub.py: print("sub.py") => sub.py: def subFunc() [keine Ausgabe, da Prozedur - kein return]
woher kommt das zweite 'haupt.py' an der dritten Stelle ?

Einleuchten würde mir:
zuerst Anweisungen von haupt.py abarbeiten, dann import-Anweisung abarbeiten:
haupt.py: print("haupt.py") => haupt.py: def hauptFunc() [erzeugt keine Ausgabe, da es eine Prozedur ist - kein return] => haupt.py: import sub (erster Import) => sub.py: print("sub.py") => sub.py: def subFunc() [keine Ausgabe, da Prozedur - kein return] => sub.py: import haupt (erster Import) => haupt.py: import sub (zweiter Import [keine Bearbeitung/Ausgabe, da bereits eingebunden]) => haupt.py: print("haupt.py") => haupt.py: def hauptFunc() [erzeugt keine Ausgabe, da es eine Prozedur ist - kein return]
=> dann hätte ich als Ausgabe:
haupt.py
sub.py
haupt.py

es sind mal wieder die "kleinen" Dinge, die mich verzweifeln lassen oder mein Verständnis durcheinanderbringen.

Edit:
aha, du hast einen Zusatz gemacht. Nun, wenn es zweimal existiert, dann weiß ich auch, woher das zweite "haupt.py" kommt, einmal von main() und einmal von haupt.py, oder ?
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: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Perlchamp: wie __blackjack__ schon geschrieben hat, wird `haupt.py` einmal als __main__ ausgeführt und einmal als haupt importiert, daher zweimal das Print..
Alfons
User
Beiträge: 7
Registriert: Montag 18. Februar 2019, 16:08

Vielen Dank für die schnelle und umfassende Antwort.
Hintergrund meiner Frage war u.a. folgende Konstellation:
Eine Datei für die Windowsoberfläche mit Buttons etc.
Eine Datei für die Ereignis-/Commandfunktionen.
Eine Datei für diverse Berechnungen etc.
Alle Dateien sind miteinander "verzahnt".
Von der Oberflächendatei werden Funktionen der Eventdatei benutzt.
Die Eventdatei greift die Widgets die Oberfläche zu (umkonfigurieren, sperren, etc.).
In der Eventdatei werden Berechnungsfunktionen der Kalkulationsdatei benutzt.

Dann ist diese Aufgabenstellung gar nicht so schnell mit ein paar wenigen imports zu lösen.

Werde ich schon irgendwie hinkriegen.

Gruß
Alfons
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Alfons: Das Event-Modul sollte das Oberflächenmodul nicht benötigen. Das würde ja sowieso nur gehen wenn da irgendwo globale Variablen existieren würden, was auch nicht sein sollte. Zudem fasst man die GUI-Elemente und die Ereignisbehandlung üblicherweise in einer Klasse zusammen, das lässt sich ja auch nicht wirklich auf zwei Module verteilen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Alfons
User
Beiträge: 7
Registriert: Montag 18. Februar 2019, 16:08

Ja, macht Sinn.
Danke
Alfons
Alfons
User
Beiträge: 7
Registriert: Montag 18. Februar 2019, 16:08

Ich hätte noch einen Nachbrenner.
Gibt es eine kurze, elegante Lösung, für den Datenaustausch / Funktionsaufruf zwischen zwei Dateien im gleichen Verzeichnis?
Beispiel: In einer Datei habe ich Funktionen für statischtische Berechnungen (stat.py). In einer andereren Datei meine Datenbankfunktionen (base.py).
Base.py benötigt immer wieder mal stat.py - Funktionen und umgekehrt.
Alles in eine Datei packen möchte ich nicht. Ich werde daher immer mit dem o.g. Konflikt leben müssen?
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst nicht alles in eine Datei schreiben. Aber wenn Module sich gegenseitig importieren müssen, ist das ein Entwurfsfehler. Es ist schwer eine allgemeine Lösung dafür vorzuschlagen, dazu muss man den Code sehen. Aber bei mir kommt so ein Fall auch bei tausenden Zeilen Code und Dutzenden Modulen nicht vor.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Alfons: Wenn die sich tatsächlich immer mal wieder Gegenseitig benötigen, dann gehören sie wie gesagt nicht in verschiedene Module. Wobei ich mich Frage wozu statistische Berechnungen Datenbankfunktionen brauchen. So einer Berechnung sollte doch egal sein wo die Daten her kommen. Man sollte die ja auch beispielsweise ohne Datenbank testen können. Und umgekehrt sollte die Datenhaltungsschicht keine Berechnungen der Programmlogik benötigen. Das klingt so ein bisschen als wäre das falsch aufgeteilt worden.

Datenaustausch zwischen Dateien gibt es nicht. Das sind Module. Und auch da stellt sich die Frage nicht, denn Funktionen/Methoden sollten alles was sie benötigen (ausser Konstanten) als Argument(e) übergeben bekommen. Dann ist auch völlig egal ob sie im gleichen Modul oder verschiedenen Modulen definiert werden.

Ich hoffe die beiden Module sind in einem Package definiert, sonst gibt es Probleme wenn Du oder irgendein verwendetest Modul das `stat`-Modul aus der Standardbibliothek benötigt. Programme oder Bibliotheken die aus mehr als einem Modul bestehen, fasst man am besten in einem Package zusammen, so das ”auf oberster Ebene” nur der eine Packagename mit allen anderen Modul- und Packagenamen konkurriert.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Alfons
User
Beiträge: 7
Registriert: Montag 18. Februar 2019, 16:08

Danke jetzt habe ich die Thematik verinnerlicht.
Grüße
Alfons
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Alfons: Strukturierung ist zu anfangs nicht so einfach. Nimm einfach drei Module. Eins für die Datenbankanbindung, eins für die Statistikfunktionen und dann das Hauptmodul, das sich der beiden anderen bedient. Dann hast Du auch keine zirkulären Importe.
Antworten