Projektorganisation

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
pymue
User
Beiträge: 11
Registriert: Dienstag 14. Oktober 2014, 08:38

Hallo,

hab grad einen dicken Knoten im Kopf ... ich komme von der C++-Welt, daher wohl auch meine falsche Denke.

Wenn ich ein Projekt mit mehreren Dateien habe, macht es sicherlich Sinn, diese in einer gewissen Ordnerstruktur zu organisieren. Nun war meine Idee, dass alle Module in einem Unterordner abgelegt werden, und alle direkt ausführbaren Dateien auf der höchsten Ebene:

helloworld.py
subfolder
MyText.py -> enthält getMyText()
MyString.py -> enthält getMyString()

Um über helloworld.py eine Funktion aus MyText.py auszuführen, muss diese mit "from subfolder.MyText import getMyText" eingebunden werden. Bis hierhin ist auch alles klar.
Nun aber angenommen dass MyText.py auch noch MyString.py verwendet. Dann habe ich das Problem, dass je nachdem ob ich helloworld.py oder MyText.py ausführen möchte verschiedene Art und Weisen nötig sind, MyString einzubinden. Das Ausführen von helloworld.py erfodert in MyText.py "from subfolder.MyString import getMyString", das Ausführen von MyText.py (z.B. zum Test mit if __name__ == "__main__") erfordert jedoch "from MyString import getMyString".

Oder ist der Ansatz schlicht und einfach falsch?
Ich habe auch von dem __init__.py gelesen, dass wohl im subfolder enthalten sein sollte. Das geschilderte Problem lässt sich damit aber nicht lösen. ???

MfG pymue
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Es ist nicht schlimm, wenn zwei unterschiedliche Wege zum gleichen Objekt führen. Der eigentliche Import wird von Python dabei nur einmal durchgeführt. Das importierte Modul wird anschließend in ``sys.modules`` gecached und für nachfolgende Importe wiederverwendet.

Böse wird's erst bei zirkulären Abhängigkeiten, d.h. wenn Modul A etwas aus Modul B verwendet *und* Modul B etwas aus Modul A verwendet.
pymue
User
Beiträge: 11
Registriert: Dienstag 14. Oktober 2014, 08:38

hmm, aber ich bekomme doch ein ImportError, wenn ich den falschen Weg verwende.

???

MfG pymue
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Du hast vorher von *unterschiedlichen* Wegen gesprochen und nichts von einem ``ImportError`` erwähnt. Dieser Fehler tritt naheliegenderweise auf, wenn du das Objekt auf eine falsche Art importiert hast. Das hat aber nichts mit der Tatsache zu tun, dass durchaus mehrere Import-Wege zum selben Ziel führen könnnen.
BlackJack

@pymue: Das Problem ist, dass Du anscheinend das Arbeitsverzeichnis zu dem Skript *in* dem Package wechselst. Das ist ein „no go” weil es eben genau zu diesen (und weiteren) Problemen führt. Wenn Du ``my_text.py`` als Skript ausführen möchtest, dann wechsle in das Verzeichnis in dem ``subfolder/`` liegt, oder sorge anderweitig dafür das ``subfolder/`` im Suchpfad für Importe liegt, und starte es mit ``./subfolder/my_text.py``, oder mit ``python -m subfolder.my_text``.

Lesestoff zur Schreibweise von Namen: Style Guide for Python Code.

``subfolder`` ist hier ja nur ein Beispiel, aber ich erwähne trotzdem mal dass das Teil des Packages ist, also einen vernünftigen Namen braucht der als Modulname Sinn macht. Und eventuell mag man auch etwas Code in dieses Package/Modul stecken, also zum Beispiel `__version__` und `__author__`. Wenn die öffentliche API nicht zu umfangreich ist, wird die auch oft über das Package zur Verfügung gestellt, damit Benutzer nicht die anderen Module importieren müssen.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wie lautet denn der Fehler? Kann es sein, dass du gerade Module und Packages durcheinander wirfst? Denn Packages brauchen durchaus die von dir bereits ins Spiel gebrachte ``__init__.py``, damit sie funktionieren.
pymue
User
Beiträge: 11
Registriert: Dienstag 14. Oktober 2014, 08:38

es ist sehr gut möglich, dass ich grad etwas durcheinander bringe ...

... anders gefragt. Ich habe drei Dateien: "helloworld.py", "MyText.py" und "MyString.py". Die letzten beiden befinden sich in einem Unterordner "subfolder".

helloworld.py:

Code: Alles auswählen

from subfolder import MyText

print(MyText.getStr())
MyText.py:

Code: Alles auswählen

import MyString

def getStr():
    return MyString.getMyString()

if __name__ == "__main__":
    print(getStr())
MyString.py:

Code: Alles auswählen

def getMyString():
    return "This is my String"
Wie muss ich nun die Import-Zeilen gestalten, damit helloworld.py und MyText.py gleichzeitig ausführbar sind?
Vielleicht noch zu den Begriffen. "helloworld.py", "MyText.py" und "MyString.py" sind Module. Der Unterordner mit den zugehörigen Dateien ist ein Package. Richtig?

MfG pymue
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ja, "subfolder" ist ein package. Aber nur, wenn es auch eine Datei namens "__init__.py" gibt: https://docs.python.org/2/tutorial/modu ... l#packages
BlackJack

@pymue: Ich habe im Grunde ja schon geschrieben was man machen muss, bzw. was man nicht machen darf. Zusätzlich: Pakete innerhalb eines Packages würde ich über den Packagenamen importieren, also im `my_text`-Modul ein ``from subfolder import my_string``.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

pymue hat geschrieben:Wie muss ich nun die Import-Zeilen gestalten, damit helloworld.py und MyText.py gleichzeitig ausführbar sind?
Damit ein package oder ein Modul gefunden wird, reicht es nicht, dass in jedem package Ordner eine __init__.py Datei ist. Das root-Verzeichnis der ganzen package-Verzeichnisse muss auch in der Umgebungsvariablen PYTHONPATH drin sein.

Ausführbar heißt, Du gehst mit einer Shell in das entsprechende Verzeichnis und in diesem Verzeichnis rufst Du dann
> python helloworld.py
auf und in subfolder rufst Du
> python MyText.py
auf.

Überlege Dir auch gut, ob Du wirklich in jedem Modul so ein
if __name__ == "__main__":
Konstrukt brauchst. Wieviel main-Funktionen brauchst Du in Deinen C++ Projekten?
a fool with a tool is still a fool, www.magben.de, YouTube
BlackJack

@MagBen: Um Himmels willen *Nein*. Man führt keine Module in Packages aus in dem man das Arbeitsverzeichnis dort hin wechselt! Das Arbeitsverzeichnis ist auch Teil des Suchpfads, und zwar vor allen anderen, und damit sind alle Module *im* Package auch direkt ohne das Package importierbar, was zu Namenskollisionen führen kann und falschen Modulen die importiert werden. Wir hatten neulich gerade so einen Fall hier bei dem dann immer die falschen Module importiert wurden. Das kann man in eigenem Quelltext noch durch absolute Importe in den Griff bekommen, aber bei fremden Bibliotheken fällt ein Import *dort* auf die Nase.
pymue
User
Beiträge: 11
Registriert: Dienstag 14. Oktober 2014, 08:38

@BlackJack: Deine Antwort hatte ich ganz übersehen. Ich hab mir schon gedacht, dass es auf das Arbeitsverzeichnis ankommt.
Aber dennoch gibt es ja sicherlich irgendwann einmal Abhängigkeiten innerhalb eines Packages. Wie werden die dann "verlinkt"? ... hmmm, wenn man sowieso immer von aussen testet (in dem ordner der subfolder enthält), dann müssen sich die Module selbst ja gar nicht gegenseitig kennen. Richtig?

P.S. Dieses 'if __name__ == "__main__": ' habe ich bis jetzt zum debuggen verwendet, d.h. um kleine Funktionen zu testen.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

BlackJack hat geschrieben:@MagBen: Um Himmels willen *Nein*. Man führt keine Module in Packages aus in dem man das Arbeitsverzeichnis dort hin wechselt! Das Arbeitsverzeichnis ist auch Teil des Suchpfads, und zwar vor allen anderen, und damit sind alle Module *im* Package auch direkt ohne das Package importierbar, was zu Namenskollisionen führen kann und falschen Modulen die importiert werden. Wir hatten neulich gerade so einen Fall hier bei dem dann immer die falschen Module importiert wurden. Das kann man in eigenem Quelltext noch durch absolute Importe in den Griff bekommen, aber bei fremden Bibliotheken fällt ein Import *dort* auf die Nase.
Dann eben im Root-Verzeichnis
> python helloworld.py
und
> python subfolder/MyText.py
Wird aber ziemlich sperrig bei sub-sub-sub-foldern.
Deshalb meinte ich, nicht überall muss eine Main-Funktion rein.
a fool with a tool is still a fool, www.magben.de, YouTube
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

pymue hat geschrieben:P.S. Dieses 'if __name__ == "__main__": ' habe ich bis jetzt zum debuggen verwendet, d.h. um kleine Funktionen zu testen.
Das ist anfänglich auch völlig ok. Obgleich das __main__ Konstrukt eigentlich nur in Modulen vorkommen sollte, die für sich selbst ein lauffähiges Programm darstellen, deren Funktionen aber auch in andere Programme integrierbar sein sollen. Zum Testen solltest Du Dich später dann mit Unittests oder pytest vertraut machen.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ausführbare Module sollte man besser via ``python -m package.subpackage.modul`` aus dem Pfad starten, dessen Unterverzeichnis das oberste Package bildet. Dann findet Python schon von sich aus den richtigen Weg zum gewünschten Modul. Selbst in Shell-Skripten würde ich diese Art des Aufrufs bevorzugen.

Und für Skripte, die nur die ``main()``-Funktion eines Python-Programms aufrufen sollen, würde ich einfach eine ausführbare Datei in einem gesonderten Verzeichnis außerhalb der Package-Hierarchie erstellen, die das Package importiert und ``main()`` aus dem passenden Modul aufruft. Zumindest unter Linux funktioniert das mit dem passenden Shebang ohne Probleme.
Antworten