Modulasierung in Python 3

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
Alex89
User
Beiträge: 2
Registriert: Freitag 11. August 2017, 18:01

Hallo allerseits,

ich habe folgender Frage.

Wenn ich mein Projekt in Python 3 Modulieren will und entsprechend folgende Struktur hätte
main.py

mypackage/
__init__.py
mymodule.py
myothermodule.py


mypackage2/
__init__.py
mymodule2.py
myothermodule2.py
und wenn ich jetzt in mymodule2.py auf mymodule.py zugreifen will mit folgendem statement

Code: Alles auswählen

from ..mypackage import mymodule2
Dann bekomme ich folgende Fehlermeldung: Parent module '' not loaded, cannot perform relative import


Hätte jemand eine Idee woran das liegt?

Grüße
Alex
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

So solltest du das auch nicht machen. Du bewegst dich mit diesem import ja quasi aus der definierten Hierarchie heraus in ein "unbekanntes" Verzeichnis. Die Pakete koennte ja zB in verschiedenen EGGs liegen etc. Da gibt es keine Garantien, wie die zueinander liegen.

Stattdessen solltest du "from mypackage import ..." oder aehnliches machen.

Falls die beiden eng zusammengehoeren, sollten sie eh einfach in eine Pakethierarchie gesteckt werden, also einfach noch eine Ebene drueber:

Code: Alles auswählen

myapp
     mypackage/
     mypackage2/
     
Alex89
User
Beiträge: 2
Registriert: Freitag 11. August 2017, 18:01

__deets__ hat geschrieben:So solltest du das auch nicht machen. Du bewegst dich mit diesem import ja quasi aus der definierten Hierarchie heraus in ein "unbekanntes" Verzeichnis. Die Pakete koennte ja zB in verschiedenen EGGs liegen etc. Da gibt es keine Garantien, wie die zueinander liegen.

Stattdessen solltest du "from mypackage import ..." oder aehnliches machen.

Falls die beiden eng zusammengehoeren, sollten sie eh einfach in eine Pakethierarchie gesteckt werden, also einfach noch eine Ebene drueber:

Code: Alles auswählen

myapp
     mypackage/
     mypackage2/
   
Ok vielen Dank!

Habe das in einem Buch gelesen und wollte es für mein Projekt ausprobieren.

Scheint wohl noch für ein altes Python gültig gewesen zu sein.

Dann werde ich das in der Planung entsprechend mit überlegen.

Aber es ist auch generel nicht mehr möglich oder?

Grüße
Alex
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe das nie versucht, und ob dieses alte Buch das jetzt richtig beschreibt weiss ich nicht. Bzw. ob du dich da richtig erinnerst. Da muesstest du das schon vorlegen, damit man das mal beurteilen kann.

Ganz generell gilt: diese Art zu importieren hat zwar gewisse Aehnlichkeiten mit der Navigation in einem Filesystem, ist aber *kein* ersatz dafuer. Genausowenig wie du "from c:/foo/bar/baz import x" machen kannst. Python-Pakete und Module werden immer relativ zu den Eintraegen in sys.path interpretiert.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Bin da heute auch über einige Varianten gestolpert und einen Error hinter dem anderen erhalten. Das sieht doch hier so ähnlich aus oder?

https://www.python.org/dev/peps/pep-032 ... s-decision

Als ich dann die Nase voll hatte, bin ich bei dieser Variante gelandet und finde die bislang nicht schlecht. Wenn das nicht unbedingt eine Package werden soll, müsste das zur Laufzeit eigentlich genügen? Oder?
Funktioniert zumindest.

Code: Alles auswählen

# Verzeichnis movies entält: 
# szenen/muster_szene.py
# werkzeuge/movie_funktionen.py

import sys
import os

pfad = os.path.abspath(".")
sys.path.append(os.path.join(pfad, "werkzeuge"))
sys.path.append(os.path.join(pfad, "szenen"))

from muster_szene import Szene
from movie_funktionen import berechne_kreis
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Funktioniert nur durch Zufall, weil du dein Skript startest wenn du dich im gleichen Verzeichnis befindest, in dem es auch liegt.

Besser ist es, sich von der Variable "__file__" ausgehend die Pfade zusammenzubauen.

Code: Alles auswählen

base = os.path.dirname(__file__)
sys.path.append(os.path.join(base, "foo"))
Im konkreten Beispiel von dir verstehe ich nicht, warum du zwei Pfade hinzufuegst, statt dann einfach "werkzeuge" und "szenen" als Paketenamen zu nutzen.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Das mit os.path.dirname(__file__) werde ich mal testen, bisher habe ich da noch keinen Unterschied gesehen. Bei Verknüpfungen auf dem Desktop verknüpfte ich bisher auf Datei und bei Suchscript oder beim Öffnen und Speichern von Dateien stimmte es bisher. Das jetzige Verzeichnis habe ich heute getrennt und nun sieht es bislang so aus. Doch wenn das mit os.path.dirname(__file__) zuverlässiger ist, dann werde ich das ändern, weil ich es später einmal veröffentlichen möchte.

movies\movie.py
movies\werkzeuge\movie_funktionen.py
movies\szenen\muster_szene.py

Da jede Szene im Schnitt nicht länger als 20 bis 40 Sekunden werden dürfte und jede ihr eigenes kleines "Drehbuch" erhält, könnte da schon einiges mit der Zeit zusammenkommen und die sollten halt "abseits" liegen und bearbeitbar bleiben. Und bei den Funktionen bin ich mir noch nicht sicher, was da alles noch zusammen kommen könnte.
Hatte lange überlegt, wie ich da die Einstellungen für jedes Objekt übernehmen könnte und das eine war so umständlich wie das andere. Zwischendurch war ich dann mal soweit, dass ich für jede Szene die Einstellungen in einer Datenbank speichern wollte, doch die müssten ja vorher auch geschrieben werden.
Nun mache ich es ähnlich wie in Gimp, rufe entsprechend der Szene eine Funktion nach der anderen auf und wenn es gut ist, wird die Datei als szene_xy.py gespeichert und die nächste kann begonnen werden.

Eigentlich ging es mir ja nur darum, dass es halt auch eine Variante ist, auf verschachtele Verzeichnisse zuzugreifen, wenn es denn genügt.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der Unterschied ist 100%ig da, mit dem unten angegeben Skript in /tmp liefert es die Ausgabe

Code: Alles auswählen

dir@client2087 ~/Dropbox $ python /tmp/test.py 
/Users/dir/Dropbox
/tmp
Dabei stehe ich in ~/Dropbox mit der Shell.

Code: Alles auswählen

import os
print(os.path.abspath("."))
print(os.path.dirname(__file__))
Insofern ist dein Rezept abhaengig von externen Faktoren.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Gut, dann werde ich das ändern, Dein Test bringt ja einen deutlichen Unterschied.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Habe heute noch einmal einiges durchgelesen. Jetzt habe ich den Unterschied begriffen, denke ich zumindest, weil beide Varianten soweit erst einmal funktionieren. Und ich dachte man müsste in den Verzeichnissen auch noch eine __init__.py ablegen und bin dann zusätzlich noch durch diese unterschiedliche Schreibweise bei Importen durcheinander gekommen.

Mit sys.path.append() nur "from name import ...":

Code: Alles auswählen

# Verzeichnis movies entält:
# movies\movie.py
# movies\werkzeuge\movie_funktionen.py
# movies\szenen\muster_szene.py

import sys
import os
 
pfad = os.path.dirname(__file__)
sys.path.append(os.path.join(pfad, "werkzeuge"))
sys.path.append(os.path.join(pfad, "szenen"))

from muster_szene import Szene
from movie_funktionen import berechne_kreis
Mit einer leeren __init__.py mit "from verzeichnis.name import ...":

Code: Alles auswählen

# Verzeichnis movies entält:
# movies\__init__.py
# movies\movie.py
# movies\werkzeuge\movie_funktionen.py
# movies\szenen\muster_szene.py
 
from szenen.muster_szene import Szene
from werkzeuge.movie_funktionen import berechne_kreis
Antworten