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

Herzlichen Dank für Deine ausführliche Antwort - ich habe mich sehr drüber gefreut.
BlackJack hat geschrieben: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.
Graphenanzeige habe ich als ein unabhängiges "Projekt" entwickelt. Und ich habe noch weitere Elemente selber als Projekte hergestellt, z.B. eine Menüleiste. Diese einzelnen Elemente funktionierten.


Es gibt also jetzt folgende Verzeichnisse:

C:\Pythonprojekte\MeineBibliothek\graphenanzeige
C:\Pythonprojekte\MeineBibliothek\menueleiste

Ich hab aber auch im Verzeichnis C:\Pythonprojekte\MeineBibliothek\ brav eine Datei __init__.py abgelegt. Und tatsächlich existiert auch im Verzeichnis C:\Pythonprojekte\__init__.py. Und natürlich auch in C:\Pythonprojekte\MeineBibliothek\graphenanzeige\__init__.py und auch in Graphenanzeige\data\__init__.py

Also - so wie ich es jetzt kapiert habe - erstelle ich mit __init__.py ein Projekt. Wenn also Graphenanzeige ein Projekt sein soll, dann darf nicht im Verzeichnis C:\Pythonprojekte\MeineBibliothek\__init__.py vorhanden sein, weil sonst auch MeineBibliothek als ein Projekt aufgefasst wird?

Muss ich denn dann auch die Datei C:\Pythonprojekte\MeineBibliothek\graphenanzeige\data\__init__.py haben - oder dort dann nicht mehr? Also setze ich die __init__.py nur in dem Startverzeichnis von dem eigenständigen Modul/Projekt - oder auch in den dazugehörigen Unterverzeichnissen?
BlackJack

@Papp Nase: Eine ``__init__.py`` macht ein Verzeichnis nicht zu einem Projekt sondern zu einem Package, beziehungsweise Subpackage wenn im Verzeichnis darüber auch schon eine ``__init__.py`` liegt. Und so eine Datei muss in allen Verzeichnissen liegen über die man etwas importieren möchte, damit Python weiss, dass es sich um ein Package handelt und vor allem auch was denn der Inhalt des Packages selber ist, denn man kann ja nicht nur Module aus einem Package importieren, sondern auch das Package als solches. Auf oberster Ebene eines Packages würde ich da zum Beispiel mindestens Metainformationen wie Dokumentation und Attribute wie `__version__`, `__author__`, und vielleicht auch `__licence__` erwarten. Nicht selten werden in der ``__init__.py`` auch die Objekte die zur offiziellen API gehören aus den Modulen und Subpackages importiert und damit sozusagen auf dieser Ebene ”re-exportiert”.

Falls alle Deine ``__init__.py`` leer sind, könnte man das übrigens eventuell auch als vergeudeten Namensraum auffassen.

Wenn ``graphenanzeige/data/`` ein (Sub)Package sein soll, man es also selbst oder Module daraus importieren können soll, dann muss da eine ``__init__.py`` drin stehen.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Vielen Dank für Deine Antwort, über die ich mich sehr gefreut habe.
Papp Nase hat geschrieben: Auf oberster Ebene eines Packages würde ich da zum Beispiel mindestens Metainformationen wie Dokumentation und Attribute wie `__version__`, `__author__`, und vielleicht auch `__licence__` erwarten.
Diese Metainfos - __author__ = xxx - soll ich die dann am besten in die __init__.py für das jeweilige Modul reinpacken? Also z.B. in /graphenanzeige/__init__.py, weil das dann für mein Modul Graphenanzeige gelten soll?
BlackJack

@Papp Nase: Ja, diese Metainformationen findet man normalerweise in dem obersten Package eines Projekts/einer Bibliothek. Und das gilt dann für das gesamte Package inklusive Unterpackages. Die haben ja selten noch mal eigene Versionen oder Autoren.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Vielen Dank für Deine Antwort. Ich habe jetzt auch das eine oder andere zu der __init__.py gelesen. An einer Stelle hab ich es so verstanden, dass die __init__.py auch ausgeführt wird und man dort auch z.B. import-Befehle hineintun könnte, die dann für das ganze Packageprojekt gültig sein sollen.

Ich hab jetzt z.B. in die Datei /graphenanzeige/__init__.py hineingeschrieben "from graphenanzeige.data.fofo import FIFO", weil ich das auch in der Datei /graphenanzeige/start.py stehen hatte und ich dachte, wenn ich es in die __init__.py hineinschreibe, dass ich es dann nicht mehr in den einzelnen .py-Dateien machen brauche - aber das hat nicht geklappt.
BlackJack

@Papp Nase: Alles in der ``__init__.py`` gilt nur für das ”Package-Modul” und für nichts anderes. Mit einem ``from graphenanzeige.data.fofo import FIFO`` in der ``graphenanzeige/__init__.py`` erreicht man das das `FIFO`-Objekt aus `graphenanzeige.data.fofo` auch in `graphenanzeige` unter diesem Namen abrufbar ist.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

BlackJack hat geschrieben:@Papp Nase: Alles in der ``__init__.py`` gilt nur für das ”Package-Modul” und für nichts anderes. Mit einem ``from graphenanzeige.data.fofo import FIFO`` in der ``graphenanzeige/__init__.py`` erreicht man das das `FIFO`-Objekt aus `graphenanzeige.data.fofo` auch in `graphenanzeige` unter diesem Namen abrufbar ist.
Vielen Dank für Deine Antwort.

Ok, ich lasse das lieber, in diese __init__.py´s etwas reinzuschreiben, ich glaube, es ist besser, wenn ich es sauber in den Modulen direkt reinschreibe.

Ich habe jetzt aus meiner Datenstruktur C:\Pythonprojekte\MeineBibliothek\ die __init__.py gelöscht und nur noch in den einzelnen nachfolgenden Verzeichnissen wie /graphenanzeige oder /xyz eine __init__.py drinnen. Ich hab jetzt mein Arbeitsverzeichnis auf das aktuelle Projektverzeichnis gelegt, in das ich die Module hineinladen möchte und in einen anderen Verzeichnis-Strang, wo \MeineBibliothek ... drinnen ist, da habe ich den Pfad "external libs" draufgesetzt in der Programmierumgebung und jetzt findet er die Module und auch, wenn ich in dem Modul der Übersichtlichkeit halber auch wieder ein Unterverzeichnis hab und das "graphenanzeige.data.fifo" aufrufe, dann findet er es. Scheinbar lag es dann wirklich an "zu vielen" __init__.py-Dateien.
BlackJack

@Papp Nase: So wie Du das beschreibst kannst Du das Programm dann nur aus der IDE heraus starten, denn die „external libs”-Einstellung gilt ausserhalb ja nicht.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

BlackJack hat geschrieben:@Papp Nase: So wie Du das beschreibst kannst Du das Programm dann nur aus der IDE heraus starten, denn die „external libs”-Einstellung gilt ausserhalb ja nicht.
och menno, jetzt war ich grade so froh, dass ich es unter der IDE zum Laufen gekriegt habe und jetzt ist es immer noch nicht richtig :-(

Und wenn ich mein Verzeichnis /MeineBibliothek in das Verzeichnis

/python34/lib/MeineBibliothek hin kopiere? Dort sind ja auch die ganzen anderen Dinger. Ich hab auch extra jetzt geschaut, in dem Verzeichnis /Python34/lib gibt es keine __init__.py, nur in den Unterverzeichnissen wie /lib/distutils/__init__.py ...

Würde das funktionieren dann?

In der IDE gibt es diesen Pythonpath. Aber der Pythonpath ist keine Systemvariable, weil ich die nicht gefunden hab.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Papp Nase hat geschrieben:In der IDE gibt es diesen Pythonpath. Aber der Pythonpath ist keine Systemvariable, weil ich die nicht gefunden hab.
Doch, PYTHONPATH ist eine Systemvariable, das sagt sogar das Tutorial beispielsweise hier: https://docs.python.org/2/tutorial/modu ... rd-modules

Aber PYTHONPATH muss es bevor du sie erstellst nicht zwingend geben.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Vielen Dank für die Antwort.

Hab ich das jetzt richtig verstanden - wenn ich mir selber einen Pythonpath setze - bevor der Interpreter gestartet wird, dann klappert der bei der Suche nach Modulen den ganzen Pfad ab? Ich müsste mir dann "einfach" den Pythonpath so setzen:

Code: Alles auswählen

set pythonpath=C:\...\meinHauptprogramm;C:\...\meineModule;
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

cool, ich hab das mal mit dem Pythonpath grade ausprobiert - und es funktioniert :-)
Antworten