Probleme bei der Einbindung von SubModulen in ein Programm

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.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Hallo!

Ich habe mir in Python mehrere graphische Teilelemente programmiert - z.B. eine Graphenanzeige, eine Buttonleiste und noch andere Dinge. Diese einzelnen Teilelemente haben stets die gleiche Verzeichnisstruktur - nennen wir sie mal

Graphenanzeige
/a
/b
/c
start.py

Buttonleiste
/a
/b
/c
start.py

Hauptanwendung
/a
/b
/c
start.py

In den Verzeichnissen /a, /b und /c sind unterschiedliche Teile der jeweiligen Anwendung der Ordnung halber eingepflegt (/a, /b und /c sind jetzt hier nur abstrakte Bezeichnungen) und jedes Teilelement funktioniert im Test für sich auch ganz gut. Nun möchte ich sie in der Hauptanwendung zusammenführen. Dazu rufe ich die Startklasse in der Datei start.py auf.

Jetzt rufe ich im Hauptprogramm aus dem Modul Graphenanzeige die Startklasse auf. Alles wird gefunden - soweit ok. Aber die Startklasse selber greift wieder auf die Unterverzeichnisse /a, /b und /c zu, weil dort Unterklassen gespeichert sind. Nun scheint aber die Startklasse Graphenanzeige im Verzeichnis /Hauptanwendung/a nach dem Untermodul zu suchen und nicht im eigenen jeweiligen Unterordner /Graphenanzeige/a.

Was mache ich falsch?
BlackJack

@Papp Nase: Mindestens die drei Komponenten sollten vielleicht besser in jeweils einem Package stecken und dann absolute Importe über den Package-Namen verwenden. Dazu müssen dann natürlich auch alle drei Packages im Suchpfad für Module liegen. Vielleicht sollte man wenn das zusammen *eine* Anwendung ist, auch besser *alles* in ein Package stecken. Für den Suchpfad gilt das gleiche: Das Package muss dann natürlich da drin enthalten sein.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

BlackJack hat geschrieben:@Papp Nase: Mindestens die drei Komponenten sollten vielleicht besser in jeweils einem Package stecken und dann absolute Importe über den Package-Namen verwenden. Dazu müssen dann natürlich auch alle drei Packages im Suchpfad für Module liegen. Vielleicht sollte man wenn das zusammen *eine* Anwendung ist, auch besser *alles* in ein Package stecken. Für den Suchpfad gilt das gleiche: Das Package muss dann natürlich da drin enthalten sein.
Vielen Dank für Deine Antwort. Ich kapiere das leider grade noch nicht ganz, was ich da genau machen soll. Meine Graphenanzeige und so, die werden jetzt schon in zwei verschiedenen GUIs benutzt. Ich könnte sie natürlich alle in ein gemeinsames Projekt packen, hätte aber dann Probleme, wenn ich die Graphenanzeige später nochmal etwas verändern möchte - dann muss ich sie in beiden Projekten wieder ändern. Darum hab ich mir ein kleines eigenes Projekt Graphenanzeige gemacht, wo nur diese Anzeige ist und ich sie in den beiden anderen Programmen dann verwenden kann.

Muss ich dann z.B. aus der Graphenanzeige ein Package erst erzeugen?

Ich hab ja auch immer in jedem Verzeichnis diese __init__.py-Datei stehen, die ja leer ist. Müsste ich in die __init__.py-Datei im Verzeichnis /Graphenanzeige/__init__.py etwas reinschreiben, damit die Datei /Graphenanzeige/start.py das Modul XYZ aus dem Verzeichnis /Graphenanzeige/a dann finden kann? Ich hab auch schon das mit den Punkten vor dem Import-Befehl probiert - leider erfolglos. Zum Beispiel muss eine Datei in /Graphenanzeige/a/XYZ.PY etwas aus /Graphenanzeige/c/ABC importieren. Dieser Pfad wird, wenn ich es unter Hauptprogramm aufrufe, auch nicht mehr gefunden. Dort hab ich auch schon versucht, mit den Punkten zu arbeiten, dass ich in der Datei /Graphenanzeige/a/XYZ.PY schrieb Import ..c.ABC, weil ich dachte, dass durch die beiden Punkte genau ein Verzeichnis höher gegangen wird, dass aber auch nicht klappen tat.
BlackJack

@Papp Nase: Relativen Import ausser vielleicht den '.' würde ich nicht (mehr) benutzen. Ich referenziere da immer absolut. Also in ``Graphenanzeige/a/XYZ.PY`` ein ``from Graphenanzeige.c.ABC import whatever``. Dann ist das eindeutig was genau gemeint ist.

Wenn das schon Packages sind, dann musst Du ja nur noch dafür sorgen, dass die auch im Modulsuchpfad stehen. Am besten in dem man eine ``setup.py`` schreibt und die ordentlich installiert. Entweder systemglobal, oder für den Benutzer, oder in einer virtualenv-Umgebung. Mit den `setuptools` oder `pip` kann man auch eine „bearbeitbare Installation” machen. Näheres in deren Dokumentation.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

BlackJack hat geschrieben:@Papp Nase: Relativen Import ausser vielleicht den '.' würde ich nicht (mehr) benutzen. Ich referenziere da immer absolut. Also in ``Graphenanzeige/a/XYZ.PY`` ein ``from Graphenanzeige.c.ABC import whatever``. Dann ist das eindeutig was genau gemeint ist
Vielen Dank für Deine Antwort. Das mit dem Punkt hab ich ja auch probiert, weil ich ja nur "eine" Verzeichnisebene höher rutschen wollte, wenn ich z.B. in der Datei /Graphenanzeige/a/xyz.py das Modul /Graphenanzeige/b/abc.py aufrufen will, aber das ging nicht

xyz.py:

from .b.abc.myclass import MyClass - das Modul wird einfach nicht gefunden, auch nicht mit zwei Punkten:
from ..b.abc.myclass import MyClass
BlackJack

@Papp Nase: Ich würde es wie gesagt absolut machen, ohne führende(n) Punkt(e).
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Papp Nase hat geschrieben:[...] Dazu rufe ich die Startklasse in der Datei start.py auf.

Jetzt rufe ich im Hauptprogramm aus dem Modul Graphenanzeige die Startklasse auf. [...]
Was verstehst du unter einem "Aufruf der Startklasse"?

Folgendes meinst du sicher nicht:

Code: Alles auswählen

>>> class Start():
...     def __call__(self):
...         print('Yikes!')
...         
>>> Start()()
Yikes!
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

also, ich bin jetzt echt am Verzweifeln mit dem Einbinden von Modulen. Jetzt hab ich es absolut gemacht mit folgender Verzeichnisstruktur

Hier ein Beispiel:

Code: Alles auswählen

/Arbeitsverzeichnis
        __init__.py
	/Projekt
		__init__.py
		/a
			__init__.py
			start.py:     from toola.a.toola import A
                                      A()
	/mylib
		__init__.py	
		/toola
			__init__.py
			/a
				__init__.py
				toola.py:	from b.classb import B
						class A:
						    def __init__(self):
							print ("in classA")
                                                        B()
			/b
				__init__.py
				classb.py:	class B:
						    def __init__(self):
                                                        print ("in classB")
Bin ich jetzt im Modul toola und starte toola.py geht das hier ohne Probleme. Bin ich nun im Projekt /Projekt, dann findet er so die Datei toola.py und führt sie auch aus, aber der Import in toola.py von classb schlägt fehl. Jetzt hab ich es gesomacht, dass ich das Verzeichnis /mylib als "external-lib" gekennzeichnet habe. Dann habe ich den Import-Befehl von toola.py abgewandelt zu:

Code: Alles auswählen

from tool1.b.b import B
- und siehe, das Programm läuft nun so "fehlerfrei".

Starte ich nun aber wieder tool1 dierekt im Projekt tool1, dann geht es so nicht mehr. Was soll ich denn nun tun? Soll ich das Konstrukt machen:

Code: Alles auswählen

try:
   from tool1.b.b import B
except:
   from b.b import B
Das ist ja echt zum Haareraufen. Wahrscheinlich ist es besser, man programmiert am besten garnicht übersichtlich mit mehreren Unterverzeichnissen, sondern packt jedes Modul einfach in eine riesengroße unübersichtliche Datei. Dann hat man nur eine Datei und nicht das Problem. Die Datei von Tkinter oder die Matplotlib haben ja auch 2000 Zeilen und mehr hab ich gesehen, als ich mir diese Dateien ja anschaue - also, warum es nicht so machen, wie die Profis, die diese Module programmiert haben?
Zuletzt geändert von Papp Nase am Dienstag 23. September 2014, 20:40, insgesamt 1-mal geändert.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Jetzt rufe ich im Hauptprogramm aus dem Modul Graphenanzeige die Startklasse auf. [...]
Was verstehst du unter einem "Aufruf der Startklasse"?
[/quote]

Ich hab z.B. ein Graphenmodul gebastelt, dass eine Startklasse hat, die im Verzeichnis /graphenanzeige/a liegt. Mit dieser Klasse definiere ich mir mein Graphenfenster, übergebe alle Parameter ... - also die Klasse, mit der ich mein Modul starte. Und die Startklasse in /graphenanzeige/a braucht dann selbst wieder andere Klassen, die der Übersichtlichkeit halber in den anderen Unterverzeichnissen geanordnet sind. Bei manchen Modulen ist die Startklasse auch direkt in /... und nicht in /.../a, aber die Startklasse ruft dann eben auch wieder Elemente in /../a /../b ... auf, also auch das gleiche Problem mit den Pfaden :-(
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

BlackJack hat geschrieben:Wenn das schon Packages sind, dann musst Du ja nur noch dafür sorgen, dass die auch im Modulsuchpfad stehen. Am besten in dem man eine ``setup.py`` schreibt und die ordentlich installiert. Entweder systemglobal, oder für den Benutzer, oder in einer virtualenv-Umgebung. Mit den `setuptools` oder `pip` kann man auch eine „bearbeitbare Installation” machen. Näheres in deren Dokumentation.
Ist das hier dann besser, was Du mir da vorschlägst? Ich hab halt das Problem, dass ich die Sachen immer mal auf unterschiedlichen Rechnern bearbeiten (muss), und da wäre es cool, wenn ich immer mein /Arbeitsverzeichnis auf einen Datenträger hin- und hertransportieren kann. Wenn ich jetzt mit diesen Setup-Dingern was mache und es dann in den Verzeichnissen bei C:\python31\... installiert wird, wo auch die Matplotlib und so sind, dann muss ich den Kram ja immer kompliziert auf jedem Rechner immer neu installieren bzw. anpassen, wenn ich dann ein Update z.B. von der Graphenanzeige hab. Darum hab ich vor diesen setuptools und dem pip Angst.
BlackJack

@Papp Nase: Du solltest Dir vielleicht mal in Ruhe durchlesen wie das mit Importen funktioniert. Und ganz wichtig: setze das Arbeitsverzeichnis nicht in eine Package-Struktur und starte dann von dort Module in einem Package oder Subpackage als Programm. Da das aktuelle Arbeitsverzeichnis auch im Suchpfad ist bekommst Du ein nettes durcheinander weil Module dann unter verschiedenen Namen/Pakethierarchien erreichbar sind, und falsche Module importiert werden können. Bei dem gezeigten Beispiel darfst Du das aktuelle Arbeitsverzeichnis zum Beispiel nicht unterhalb von `Arbeitsverzeichnis/` haben. In der Konsole kannst Du in das Verzeichnis wechseln in dem `Arbeitsverzeichnis/` liegt, aber bloss nicht tiefer rein gehen. Von *dort* aus kannst Du Module als Programme starten, entweder mit Pfadangabe, oder aber mit der `-m`-Option von Python. Um zum Beispiel `toola.py` als Programm zu starten, ginge ``python -m Arbeitsverzeichnis.mylib.toola.a.toola``. Und die erste Zeile in dem Modul sollte dann ``from Arbeitsverzeichnis.mylib.toola.b.classb import B`` heissen. An dieser Stelle hoffe ich mal ganz inständig diese umfangreiche, und tiefe Package-Struktur ist hier nur als Beispiel gewählt und Du hast das nicht tatsächlich so verschachtelt.

Dein Arbeitsverzeichnis auf einem Datenträger hin- und hertransportieren? Bitte sag mir dass Du das machst weil die Rechner nicht per Netzwerk verbunden sind, und das auf dem Datenträger ein Git- oder Mercurial-Repository ist, und Du nicht tatsächlich das Programm immer hin und her kopierst‽

Man muss Pakete ja nicht global installieren. Man kann es auch für den Benutzer machen, keine Ahnung wo das dann bei Windows landet, bei Linux ist das dann irgendwo im Heimatverzeichnis, oder eben als „editierbare Installation”, das heisst die Dateien bleiben da von wo man sie ”installiert”, sind dann aber von jedem Python-Programm importierbar als wären sie richtig installiert. Das kann beim Entwickeln nützlich sein.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

BlackJack hat geschrieben:
Dein Arbeitsverzeichnis auf einem Datenträger hin- und hertransportieren? Bitte sag mir dass Du das machst weil die Rechner nicht per Netzwerk verbunden sind, und das auf dem Datenträger ein Git- oder Mercurial-Repository ist, und Du nicht tatsächlich das Programm immer hin und her kopierst‽

.
Mein Rechner zu Hause hängt natürlich nicht am Netzwerk von der Arbeit.

Ich hab auf dem Rechner an der Arbeit ein Verzeichnis c:\pythonprojekte und auch zu Hause einen Ordner. Wenn ich an der Arbeit fertig bin, dann kopiere ich das ganze Verzeichnis auf den tragbaren Datenaustauschträger und kopiere es zu Hause in den Ordner hinein, vorher wird der alte Inhalt immer gelöscht. Das hat bis jetzt immer gut funktioniert und ich setze von der IDE immer den Workspace in das Basisverzeichnis. Es sind bis jetzt keine großen Datenmengen, darum halte ich ein Versionskontrollprogramm dafür mit Kanonen auf Spatzen geschossen. Auch hab ich so immer zwei getrennte Orte, an denen die aktuelle Version gespeichert ist - einmal auf dem Datenträger und auf dem Rechner, wo ich zu letzt gearbeitet hab.
BlackJack

@Papp Nase: Argh, also das nahezu schlimmstmögliche was man machen kann.

Versionskontrollssystem hat überhaupt nichts mit der Datenmenge zu tun, sondern dass man die Entwicklung nachvollziehen kann und zu früheren Zeitpunkten zurückkehren kann, und in diesem Fall auch, dass die Daten sicherer vor versehentlichem überschreiben sind, und man nicht durcheinander kommt an welcher Stelle die aktuelleren Daten liegen.

Mit einem verteilten Versionskontrollsystem hast Du bei der Datenträgerlösung eine Kopie an drei verschiedenen Orten: Auf dem Rechner zuhause, auf dem Datenträger, und auf dem Rechner auf Arbeit. Und nicht nur eine Kopie des aktuellen Zustands, sondern an allen drei Speicherorten die komplette, aufgezeichnete Geschichte. Und nicht nur eine Variante, sondern auch noch so viele „branches” wie Du magst. Und die Kopien auf den beiden Rechnern können auch nicht ”auseinanderdriften”. Selbst wenn Du in beiden Änderungen vorgenommen hast die noch nicht kopiert wurden, hilft Dir die Versionskontrolle dabei das Du nicht manuell herausfinden musst was in welche Richtung kopiert werden muss, und Du kannst dabei weder etwas vergessen, noch etwas aus versehen einfach überschreiben.

Und das ist bei Verzeichnisstrukturen wie Du sie gezeigt hast ganz sicher nicht mit Kanonen auf Spatzen geschossen. Für die Grösse gibt es keine Untergrenze, bzw. wenn man ein Verzeichnis anlegt und dort drin eine Datei erstellt, dann ist die Untergrenze bereits erreicht, solange das nicht 100%ig sicher ein Einmal-Wegwerfskript ist.

Das mit dem Datenträger kann man machen, ich würde aber mindestens zusätzlich versuchen einen Rechner ans Netz zu bekommen der einen SSH-Zugang hat, wo man dann über eine SSH-Verbindung von zu Hause und auf der Arbeit an ein Repository heran kommt. Sachen die ich keinen ”fremden” Servern anvertrauen möchte, landen bei mir auf einem RaspberryPi der per dynamischer IP vom Netz aus per SSH erreichbar ist.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Vielen Dank für die Vielzahl Deiner Antworten.
Papp Nase hat geschrieben:Das mit dem Datenträger kann man machen, ich würde aber mindestens zusätzlich versuchen einen Rechner ans Netz zu bekommen der einen SSH-Zugang hat, wo man dann über eine SSH-Verbindung von zu Hause und auf der Arbeit an ein Repository heran kommt. Sachen die ich keinen ”fremden” Servern anvertrauen möchte, landen bei mir auf einem RaspberryPi der per dynamischer IP vom Netz aus per SSH erreichbar ist.
Das klingt cool. Ich habe gesehen, dass die Eclipse-Umgebung, in der ich mit pydev programmiere, auch anscheinend schon dieses git eingebaut hat. Ich hab halt noch nie mit Versionskontrollsystemen gearbeitet, von daher ist das auch totales Neuland für mich. Das andere System Mercurial klingt auch interessant. Wie kriegst Du denn die dynamische IP heraus - hast Du auch eine eigene Homepage bzw. im Internet einen DNS-Dienst von nem Webanbieter, damit Du die dynamische IP weisst?

Aber damit möchte ich mich momentan noch nicht so tief befassen, ich muss das erstmal mit den Pfaden hinbekommen, dass ich mein Programm wieder zum Laufen bekomme :-(
Papp Nase hat geschrieben:@Papp Nase: Du solltest Dir vielleicht mal in Ruhe durchlesen wie das mit Importen funktioniert. Und ganz wichtig: setze das Arbeitsverzeichnis nicht in eine Package-Struktur und starte dann von dort Module in einem Package oder Subpackage als Programm. Da das aktuelle Arbeitsverzeichnis auch im Suchpfad ist bekommst Du ein nettes durcheinander weil Module dann unter verschiedenen Namen/Pakethierarchien erreichbar sind, und falsche Module importiert werden können. Bei dem gezeigten Beispiel darfst Du das aktuelle Arbeitsverzeichnis zum Beispiel nicht unterhalb von `Arbeitsverzeichnis/` haben. In der Konsole kannst Du in das Verzeichnis wechseln in dem `Arbeitsverzeichnis/` liegt, aber bloss nicht tiefer rein gehen. Von *dort* aus kannst Du Module als Programme starten, entweder mit Pfadangabe, oder aber mit der `-m`-Option von Python. Um zum Beispiel `toola.py` als Programm zu starten, ginge ``python -m Arbeitsverzeichnis.mylib.toola.a.toola``. Und die erste Zeile in dem Modul sollte dann ``from Arbeitsverzeichnis.mylib.toola.b.classb import B`` heissen. An dieser Stelle hoffe ich mal ganz inständig diese umfangreiche, und tiefe Package-Struktur ist hier nur als Beispiel gewählt und Du hast das nicht tatsächlich so verschachtelt.
Ich muss das erst mal verstehen und wenn das klappt, dann kann ich über Versionsverwaltungstools nachdenken.
BlackJack

@Papp Nase: Ich benutze einen DynDNS-Dienst von einem Webhoster. Die entsprechenden Daten habe ich beim DSL-Router eingetragen, d.h. wenn sich die IP ändert, sorgt der Router dafür das der DNS die neue IP bekommt. Dann noch ein Portforwarding vom Router auf den Raspi für SSH eingerichtet und schon kann ich mich über eine feste URL nach Hause verbinden.

Davor hatte ich mal einen cronjob auf dem Raspi laufen der alle 10 Minuten beim Router die öffentliche IP abgefragt hat, und wenn die sich seit dem letzten Abruf geändert hat, die neue auf einen Webspace hochgeladen hat. Da konnte ich dann nachsehen welche gerade aktuell war.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ich würde mir ja nicht so viel Aufwand machen und das Zeug einfach bei einem Hoster wie Github oder BitBucket hinterlegen. Daher kann ich mir von überall eine aktuelle Version ziehen :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Hyperion: Den Raspi als Homeserver ans Netz zu bringen empfand ich jetzt nicht als viel Aufwand und ich finde es sehr praktisch jederzeit nach Hause ”telefonieren” können und dort Daten ablegen und abrufen zu können, oder halt auch *irgendwas* zu machen, denn das ist ja ein kompletter Rechner/Server. Nicht alles was man so speichern möchte ist geeignet öffentlich oder bei einem Anbieter irgendwo auf der Welt, dem man vertrauen muss, abgelegt zu werden. Hier geht es ja zum Beispiel anscheinend um beruflich relevante Daten/Quelltexte. Wenn ich Quelltext der firmenintern ist, auf Github oder Bitbucket ablege, dann steinigt mich mein Chef. Zu Recht.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BlackJack hat geschrieben:Wenn ich Quelltext der firmenintern ist, auf Github oder Bitbucket ablege, dann steinigt mich mein Chef. Zu Recht.
Pauschalisieren kann man das an sich natürlich nicht - gibt ja auch Firmen, die diese Anbieter dafür nutzen. Aber sicher, für alles ist das nicht zu nutzen.

Ich habe das ja auch insbesondere erwähnt, weil der OP offenbar bei dieser Thematik noch wenig Ahnung hat. Dir ist klar, *wieso* Du ein (sicherlich spaßiges :-) ) Projekt mit dem Raspi durchgezogen hast - dem OP reicht vielleicht für diesen Fall auch eine einfachere Lösung.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Ich habe es leider immer noch nicht wirklich hinbekommen, einzelne Teilmodule in ein neues Programm einzubinden, die ich in einer separaten eigenen Bibliothek speichern möchte. Das Problem sind und bleiben die Pfadangaben bei den Importbefehlen. Kann ich beim Import-Befehl einen absoluten Pfad angeben, der bei "C:\xya\asdf\sgdffads"... beginnt bzw. bei Linux dann im Wurzelverzeichnis? Ich habe es nicht hinbekommen, mit absoluten Pfaden. Ich habe auch das mit den Pfadangaben beachtet, dass das Arbeitsverzeichnis nicht unterhalb vom Verzeichnis meiner eigenen Bibliothek sein soll.

Ist es überhaupt sinnvoll, in vielen kleinen Dateien zu programmieren, damit es übersichtlich ist? Wenn ich z.B. von meinem Graphenmodul, dass auch der Übersichtlichkeit halber aus mehreren Unterverzeichnissen besteht - die dann eben von der Hauptklasse(Startklasse) des Graphenmoduls nicht mehr gefunden werden - einfach nur in einer Datei alles reinpacken würde, dann würde es wieder klappen. Aber das ist scheußliche Programmierung, weil ich dann riesengroße Dateien kriege. Aber einige der "Profidateien", die in der Python-Lib mit dabei sind, haben ja auch mehrere tausend Zeilen - oder ist das dann doch toll, nur eine einzige Datei zu haben?

Leider ist es nun mal so, dass ein Unterverzeichnis vom Graphenmodul genauso heisst, wie ein Unterverzeichnis vom Programm, was das Modul laden soll - eben, wie in meinem Beispiel /a. Und mehrere Module wie das Graphenmodul haben ein Unterverzeichnis, dass immer den gleichen Namen hat. Aber jedes Modul soll eben nur sein eigenes Unterverzeichnis nehmen und nicht das Unterverzeichnis aus der aktuellen Anwendung, denn dann findet es die jeweilge Datei nicht.

Absolute Pfade funktionieren irgendwie bei mir nicht. Warum geht es denn in Python einfach nicht, dass ich wie bei #include "..." einien wirklichen Pfad dort hineinbaue, der eben im Wurzelverzeichnis anfängt? Wenn das klappen würde, dann wäre ich schon mal zu frieden. Aber das geht nicht.

Mein aktueller Stand ist jetzt so, dass ich zwei Verzeichnisse habe -

C:\Pythonprojekte\MeinProjekt
C:\Pythonprojekte\MeineBibliothek\...

Das Arbeitsverzeichnis setze ich auf C:\Pythonprojekt\MeinProjekt
Das Verzeichnis für ExternalLibs setze ich auf C:\Pythonprojekte\MeineBibliothek

Wenn ich nun in Meinprojekt\start.py meine Graphenanzeige aufrufe, die unter C:\Pythonprojekte\MeineBibliotkek\Graphenanzeige\graphenanzeige.py zu finden ist, dann wird die Datei gefunden. Graphenanzeige ruft aber data.fifo.py auf mit "from data.fifo import FIFO", weil es unter C:\Pythonprojekte\MeineBibliothek\graphenanzeige\data\fifo.py" gibt. Das mit den Punkten funktioniert nicht, wenn ich schreibe "from .data.fifo import FIFO" oder "from ..data.fifo import FIFO". Dabei sollten ja zwei Punkte bedeuten, dass ich ein Verzeichnis zurückgehe wie bei cd.. früher bei DOS, oder der eine Punkt, wenn sich das Verzeichnis im gleichen Verzeichnis befindet. Wenn graphenanzeige.py im gleichen Verzeichnis liegt, wie data, dann müsste eigentlich der einie Punkt ausreichen - tut er aber nicht, es kommen auch Fehlermeldungen, dass die Datei nicht gefunden wird dann.

Und ein absoluter Pfad geht auch nicht: "from Pythonprojekte.MeineBibliothek.graphenanzeige.data.fifo import FIFO". Was nützt mir auch die Doku über den Import-Befehl. Alles, was ich darüber bei google oder auch youtube-Lernvideos finde, löst nicht mein Problem mit diesen Modulpfaden, weil dort immer nur erklärt wird, wie man eben eine andere Datei einfach aufruft und das die Datei __init__.py in jedem Verzeichnis stehen muss. Und wie mache ich das mit den absoluten Pfaden, wenn ich meine Bibliothek mal unter Linux/Windows hab? Dann muss ich ja auch wieder ggf. alle Pfade manuell anpassen - von der Basis her ausgehend.

Ich bin da echt am Verzweifeln, weil ich nun schon zwei Tage nur daran hänge, einfach mein Modul graphenanzeige - was schon fertig programmiert ist und funktioniert, in meiner anderen Anwendung aufzurufen. Das ist echt deprimierend :-(
BlackJack

@Papp Nase: Ich denke Du vermischst Package-Namen zu sehr mit Pfadangaben. Der Wunsch nach einem ``#include`` welches tatsächlich einen Pfadnamen im Dateisystem bekommt ist gar keine gute Idee. Python-Module und -Packages entsprechen zwar im einfachsten Fall der Verzeichnisstruktur, müssen sie aber nicht. Man kann ``import`` nahezu beliebig erweitern damit man auch Packages/Module aus einer Datenbank importieren kann, oder über das Netz, oder… — was immer man sich da so ausdenken mag.

Du musst dafür sorgen das a) alles was zusammengehört und aus mehr als einem Modul besteht in einem Package steckt, b) nichts durch eine `__init__.py` zu einem Package gemacht wird was keines ist, c) das alle Verzeichnisse in denen ein Package liegt in den Suchpfaden für ``import`` liegen, d) alle Importe absolut gemacht werden, also immer den gesamten Weg von der jeweiligen Packagewurzel angeben, und e) das aktuelle Arbeitsverzeichnis nicht innerhalb irgendeines Packages liegt.

Bei b) denke ich an das ``Arbeitsverzeichnis/`` aus Deinem Beispiel das dadurch nur *ein* Package hat in dem alles andere als Subpackages liegt, wo Du aber eigentlich ja gesagt hast das sind unabhängige Projekte.

Punkt c) kann man auf verschiedenen Wegen erreichen. Alle Module und Packages die im aktuellen Arbeitsverzeichnis liegen werden gefunden. Man kann Module und Packages an verschiedene Orte installieren. Man kann eine Umgebungsvariable setzen. Am einfachsten wäre wohl die erste Variante: alles was man nicht installieren möchte, im aktuellen Arbeitsverzeichnis halten.

Viele kleine Dateien machen nur Sinn wenn es dadurch tatsächlich übersichtlich wird. Eine Klasse pro Datei würde IMHO keinen Sinn machen, weil dadurch das Modul als Namensraum vollkommen entwertet wird. Modullänge in LOC hängt auch so ein bisschen davon ab was da drin steckt. Ich würde spätestens so ab 1000 Zeilen mal schauen ob man das thematisch in zwei oder drei Module teilen kann. Oft kann man, manchmal findet man aber auch keine saubere Trennung oder eine bei der nur eine handvoll Zeilen in einem neuen Modul landen würden. Das lohnt dann auch nicht wirklich.

Arbeitsverzeichnis auf ``C:\Pythonprojekt\MeinProjekt`` zu setzen ist schon keine gute Idee weil die Dateien und Module ``C:\Pythonprojekte\MeineBibliothek`` dann, zumindest dadurch, nicht im Suchpfad sind und alles was in ``MeinProjekt/`` liegt Vorrang hat vor gleichlautenden Modulen/Packages die anderweitig installiert/erreichbar sind. Genau deshalb sollte man ja alles in Packages stecken, damit man für solche Probleme möglichst wenig Angriffsfläche bietet.

Zwei Punkte bedeutet nicht ein Verzeichnis höher sondern ein Package höher. Wie gesagt, das muss nicht das selbe sein. Es gibt auch so etwas wie Namespace Packages, da können Subpackages sonst wo auf dem Datenträger liegen.

``from Pythonprojekte.MeineBibliothek.…`` geht nicht weil in ``Pythonprojekte/`` doch hoffentliche keine ``__init__.py`` existiert. Das ist kein Package, beziehungsweise sollte keines sein. Und wenn doch dann ginge das nur wenn das Verzeichnis in dem ``Pythonprojekte/`` liegt, also das C:\-Laufwerk im Suchpfad für Module liegt. Das Wurzelverzeichnis der C-Partition gehört da sicher nicht rein.
Antworten