Relative Importe in Python 3

713

Ich möchte eine Funktion aus einer anderen Datei im selben Verzeichnis importieren.

Manchmal funktioniert es bei mir mit, from .mymodule import myfunctionaber manchmal bekomme ich ein:

SystemError: Parent module '' not loaded, cannot perform relative import

Manchmal funktioniert es mit from mymodule import myfunction, aber manchmal bekomme ich auch:

SystemError: Parent module '' not loaded, cannot perform relative import

Ich verstehe die Logik hier nicht und konnte keine Erklärung finden. Das sieht völlig zufällig aus.

Könnte mir jemand erklären, was die Logik dahinter ist?

John Smith Optional
quelle
76
Dies bedeutet, dass Sie ein Modul innerhalb des Pakets als Skript ausführen. Führen Sie Skripte nur von außerhalb des Pakets aus.
Martijn Pieters
3
Wahrscheinlich sollten Sie die Bedingungen definieren, unter denen Sie die "manchmal" genannten Bedingungen haben. Ich verstehe, Sie meinen nicht, dass Sie zufällige Fehler haben.
Joaquin
15
@MartijnPieters: Nun, leider muss dieses Modul im Paket enthalten sein und manchmal auch als Skript ausgeführt werden können. Irgendeine Idee, wie ich das erreichen könnte?
John Smith Optional
22
@JohnSmithOptional: Das Mischen von Skripten in Paketen ist schwierig und sollte nach Möglichkeit vermieden werden. Verwenden Sie ein Wrapper-Skript, das das Paket importiert und stattdessen Ihre 'scripty'-Funktion ausführt.
Martijn Pieters
3
Scheint unglücklich. Ich habe ein Kernmodul mit Klassen / Methoden erstellt, die eine bestimmte Art von Datei analysieren / analysieren können, und ich habe auch (hauptsächlich für mich selbst) separate sekundäre Module und Skripte, die sie importieren - diese können diese Dateien massieren / konvertieren. Ich möchte aber auch in der Lage sein, diese einzelne Kerndatei (nicht ein ganzes komplexes Paket) dem Endbenutzer zu übergeben, damit dieser sie einfach neben seiner Datei platzieren und ausführen kann. In diesem "Skriptmodus" analysiert und analysiert es die Datei und die Codierung, fasst verschiedene Felder / Werte / Sonderzeichen zusammen und gibt einen Bericht. Die Datei wird jedoch nicht geändert. Anti-Muster?
Jon Coombs

Antworten:

528

Leider muss sich dieses Modul im Paket befinden und manchmal auch als Skript ausgeführt werden können. Irgendeine Idee, wie ich das erreichen könnte?

Es ist durchaus üblich, ein solches Layout zu haben ...

main.py
mypackage/
    __init__.py
    mymodule.py
    myothermodule.py

... mit so einem mymodule.py...

#!/usr/bin/env python3

# Exported function
def as_int(a):
    return int(a)

# Test function for module  
def _test():
    assert as_int('1') == 1

if __name__ == '__main__':
    _test()

... myothermodule.pyso etwas ...

#!/usr/bin/env python3

from .mymodule import as_int

# Exported function
def add(a, b):
    return as_int(a) + as_int(b)

# Test function for module  
def _test():
    assert add('1', '1') == 2

if __name__ == '__main__':
    _test()

... und main.pyso ...

#!/usr/bin/env python3

from mypackage.myothermodule import add

def main():
    print(add('1', '1'))

if __name__ == '__main__':
    main()

... was gut funktioniert, wenn Sie ausführen main.pyoder mypackage/mymodule.py, aber mypackage/myothermodule.pyaufgrund des relativen Imports fehlschlägt ...

from .mymodule import as_int

Die Art und Weise, wie Sie es ausführen sollen, ist ...

python3 -m mypackage.myothermodule

... aber es ist etwas ausführlich und passt nicht gut zu einer Shebang-Linie wie #!/usr/bin/env python3.

Die einfachste Lösung für diesen Fall, vorausgesetzt, der Name mymoduleist global eindeutig, besteht darin, die Verwendung relativer Importe zu vermeiden und nur ...

from mymodule import as_int

... obwohl, wenn es nicht eindeutig ist oder Ihre Paketstruktur komplexer ist, Sie das Verzeichnis, in dem sich Ihr Paketverzeichnis befindet, einschließen müssen PYTHONPATH, und dies folgendermaßen tun ...

from mypackage.mymodule import as_int

... oder wenn Sie möchten, dass es "out of the box" funktioniert, können Sie den PYTHONPATHIn-Code zuerst mit diesem Frob ...

import sys
import os

PACKAGE_PARENT = '..'
SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT)))

from mypackage.mymodule import as_int

Es ist eine Art Schmerz, aber es gibt einen Hinweis darauf, warum in einer E-Mail, die von einem bestimmten Guido van Rossum geschrieben wurde ...

Ich bin -1 in diesem und in jedem anderen vorgeschlagenen Twiddling der __main__ Maschinerie. Der einzige Anwendungsfall scheint darin zu bestehen, Skripte auszuführen, die sich zufällig im Verzeichnis eines Moduls befinden, das ich immer als Antimuster gesehen habe. Um mich dazu zu bringen, meine Meinung zu ändern, müsstest du mich davon überzeugen, dass es nicht so ist.

Ob das Ausführen von Skripten in einem Paket ein Antimuster ist oder nicht, ist subjektiv, aber ich persönlich finde es in einem Paket, das einige benutzerdefinierte wxPython-Widgets enthält, sehr nützlich, sodass ich das Skript für jede der Quelldateien ausführen kann, um nur eine zu wx.Frameenthalten dieses Widget zu Testzwecken.

Aya
quelle
7
Ein besserer Weg, um SCRIPTDIR zu erhalten, finden Sie in einem Kommentar zum Importieren eines Moduls aus einem relativen Pfad, als os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))ob Sie sicher wären, dass Ihr Modul immer einen richtigen hat, den fileSie auch verwenden könnten os.path.realpath(os.path.dirname(__file__)).
Marcz
2
Sie können Ihren PYTHONPATH erweitern, indem Sie ein kürzeres und lesbares Code-Snippet anwenden: sys.path.append( os.path.join( os.path.dirname(__file__), os.path.pardir ) )
Alex-Bogdanov
12
...which I've always seen as an antipattern.Ich verstehe nicht, wie es ein Anti-Muster ist ... Es scheint sehr praktisch zu sein, relative Importe einfach intuitiv arbeiten zu lassen. Ich möchte nur Dinge importieren können, von denen ich weiß, dass sie sich im selben Verzeichnis befinden. Ich frage mich, was seine Argumentation war
YungGun
9
Guido schlägt wieder zu: Nixing Sachen, die nützlich gewesen sein könnten. Nun, das wird nicht mehr passieren.
Javadba
4
Dies ist das Traurigste, was ich je über Python gesehen habe.
AtilioA
263

Erläuterung

Aus PEP 328

Relative Importe verwenden das Attribut __name__ eines Moduls, um die Position dieses Moduls in der Pakethierarchie zu bestimmen. Wenn der Name des Moduls keine Paketinformationen enthält (z. B. auf '__main__' gesetzt), werden relative Importe so aufgelöst, als wäre das Modul ein Modul der obersten Ebene , unabhängig davon, wo sich das Modul tatsächlich im Dateisystem befindet.

Irgendwann kollidierte PEP 338 mit PEP 328 :

... relative Importe basieren auf __name__ , um die Position des aktuellen Moduls in der Pakethierarchie zu bestimmen. In einem Hauptmodul ist der Wert von __name__ immer '__main__' , sodass explizite relative Importe immer fehlschlagen (da sie nur für ein Modul in einem Paket funktionieren).

und um das Problem zu beheben , führte PEP 366 die Variable der obersten Ebene ein __package__:

Durch Hinzufügen eines neuen Attributs auf Modulebene können mit diesem PEP relative Importe automatisch ausgeführt werden, wenn das Modul mit der Option -m ausgeführt wird. Mit einer kleinen Menge Boilerplate im Modul selbst können die relativen Importe ausgeführt werden, wenn die Datei namentlich ausgeführt wird. [...] Wenn [das Attribut] vorhanden ist, basieren relative Importe auf diesem Attribut und nicht auf dem Attribut Modul __name__ . [...] Wenn das Hauptmodul durch seinen Dateinamen angegeben wird, wird das Attribut __package__ auf None gesetzt . [...] Wenn das Importsystem einen expliziten relativen Import in einem Modul ohne __package__ (oder mit None) feststellt, berechnet und speichert es den korrekten Wert (__name __. rpartition ('.') [0] für normale Module und __name__ für Paketinitialisierungsmodule )

(Hervorhebung von mir)

Wenn dies der Fall __name__ist '__main__', wird eine __name__.rpartition('.')[0]leere Zeichenfolge zurückgegeben. Aus diesem Grund enthält die Fehlerbeschreibung ein leeres Zeichenfolgenliteral:

SystemError: Parent module '' not loaded, cannot perform relative import

Der relevante Teil der CPython- PyImport_ImportModuleLevelObjectFunktion :

if (PyDict_GetItem(interp->modules, package) == NULL) {
    PyErr_Format(PyExc_SystemError,
            "Parent module %R not loaded, cannot perform relative "
            "import", package);
    goto error;
}

CPython löst diese Ausnahme aus, wenn es package(den Namen des Pakets) in interp->modules(zugänglich als sys.modules) nicht finden konnte. Da sys.moduleses sich um ein "Wörterbuch handelt, das Modulnamen bereits geladenen Modulen zuordnet" , ist jetzt klar, dass das übergeordnete Modul explizit absolut importiert werden muss, bevor ein relativer Import durchgeführt wird .

Hinweis: Der Patch aus der Ausgabe 18018 hat einen weiteren ifBlock hinzugefügt, der vor dem obigen Codeausgeführt wird:

if (PyUnicode_CompareWithASCIIString(package, "") == 0) {
    PyErr_SetString(PyExc_ImportError,
            "attempted relative import with no known parent package");
    goto error;
} /* else if (PyDict_GetItem(interp->modules, package) == NULL) {
    ...
*/

Wenn package(wie oben) eine leere Zeichenfolge ist, wird die Fehlermeldung angezeigt

ImportError: attempted relative import with no known parent package

Dies wird jedoch nur in Python 3.6 oder höher angezeigt.

Lösung 1: Führen Sie Ihr Skript mit -m aus

Betrachten Sie ein Verzeichnis (das ein Python- Paket ist ):

.
├── package
│   ├── __init__.py
│   ├── module.py
│   └── standalone.py

Alle Dateien im Paket beginnen mit denselben 2 Codezeilen:

from pathlib import Path
print('Running' if __name__ == '__main__' else 'Importing', Path(__file__).resolve())

Ich füge diese beiden Zeilen nur hinzu , um die Reihenfolge der Operationen zu verdeutlichen. Wir können sie vollständig ignorieren, da sie die Ausführung nicht beeinflussen.

__init__.py und module.py enthalten nur diese beiden Zeilen (dh sie sind effektiv leer).

standalone.py versucht zusätzlich, module.py über den relativen Import zu importieren:

from . import module  # explicit relative import

Wir sind uns bewusst, dass dies /path/to/python/interpreter package/standalone.pyfehlschlagen wird. Wir können das Modul jedoch mit der -mBefehlszeilenoption ausführen , die "nach sys.pathdem benannten Modul sucht und dessen Inhalt als __main__Modul ausführt " :

vaultah@base:~$ python3 -i -m package.standalone
Importing /home/vaultah/package/__init__.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/module.py
>>> __file__
'/home/vaultah/package/standalone.py'
>>> __package__
'package'
>>> # The __package__ has been correctly set and module.py has been imported.
... # What's inside sys.modules?
... import sys
>>> sys.modules['__main__']
<module 'package.standalone' from '/home/vaultah/package/standalone.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>

-merledigt den ganzen Import für dich und setzt automatisch __package__, aber das kannst du selbst in der

Lösung 2: Stellen Sie __package__ manuell ein

Bitte behandeln Sie es eher als Proof of Concept als als tatsächliche Lösung. Es ist nicht gut für die Verwendung in realem Code geeignet.

PEP 366 bietet eine Problemumgehung für dieses Problem, ist jedoch unvollständig, da die Einstellung __package__allein nicht ausreicht. Sie müssen mindestens N vorhergehende Pakete in die Modulhierarchie importieren , wobei N die Anzahl der übergeordneten Verzeichnisse (relativ zum Verzeichnis des Skripts) ist, die nach dem zu importierenden Modul durchsucht werden.

Somit,

  1. Fügen Sie das übergeordnete Verzeichnis des N-ten Vorgängers des aktuellen Moduls hinzusys.path

  2. Entfernen Sie das Verzeichnis der aktuellen Datei aus sys.path

  3. Importieren Sie das übergeordnete Modul des aktuellen Moduls unter Verwendung seines vollständig qualifizierten Namens

  4. Stellen Sie __package__den vollständig qualifizierten Namen von 2 ein

  5. Führen Sie den relativen Import durch

Ich werde Dateien aus der Lösung Nr. 1 ausleihen und weitere Unterpakete hinzufügen:

package
├── __init__.py
├── module.py
└── subpackage
    ├── __init__.py
    └── subsubpackage
        ├── __init__.py
        └── standalone.py

Dieses Mal standalone.py importiert module.py aus dem Paket - Paket mit dem folgenden relativen Import

from ... import module  # N = 3

Wir müssen dieser Zeile den Boilerplate-Code voranstellen, damit er funktioniert.

import sys
from pathlib import Path

if __name__ == '__main__' and __package__ is None:
    file = Path(__file__).resolve()
    parent, top = file.parent, file.parents[3]

    sys.path.append(str(top))
    try:
        sys.path.remove(str(parent))
    except ValueError: # Already removed
        pass

    import package.subpackage.subsubpackage
    __package__ = 'package.subpackage.subsubpackage'

from ... import module # N = 3

Es ermöglicht uns, standalone.py nach Dateiname auszuführen :

vaultah@base:~$ python3 package/subpackage/subsubpackage/standalone.py
Running /home/vaultah/package/subpackage/subsubpackage/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/subpackage/__init__.py
Importing /home/vaultah/package/subpackage/subsubpackage/__init__.py
Importing /home/vaultah/package/module.py

Eine allgemeinere Lösung, die in eine Funktion eingeschlossen ist, finden Sie hier . Anwendungsbeispiel:

if __name__ == '__main__' and __package__ is None:
    import_parents(level=3) # N = 3

from ... import module
from ...module.submodule import thing

Lösung 3: Verwenden Sie absolute Importe und Setuptools

Die Schritte sind -

  1. Ersetzen Sie explizite relative Importe durch äquivalente absolute Importe

  2. Installieren package, um es importierbar zu machen

Beispielsweise kann die Verzeichnisstruktur wie folgt sein

.
├── project
│   ├── package
│   │   ├── __init__.py
│   │   ├── module.py
│   │   └── standalone.py
│   └── setup.py

wo setup.py ist

from setuptools import setup, find_packages
setup(
    name = 'your_package_name',
    packages = find_packages(),
)

Der Rest der Dateien wurde aus der Lösung Nr. 1 ausgeliehen .

Durch die Installation können Sie das Paket unabhängig von Ihrem Arbeitsverzeichnis importieren (vorausgesetzt, es treten keine Namensprobleme auf).

Wir können standalone.py ändern , um diesen Vorteil zu nutzen (Schritt 1):

from package import module  # absolute import

Ändern Sie Ihr Arbeitsverzeichnis in projectund führen Sie es aus /path/to/python/interpreter setup.py install --user( --userinstalliert das Paket in Ihrem Site-Packages-Verzeichnis ) (Schritt 2):

vaultah@base:~$ cd project
vaultah@base:~/project$ python3 setup.py install --user

Lassen Sie uns überprüfen, ob es jetzt möglich ist, standalone.py als Skript auszuführen :

vaultah@base:~/project$ python3 -i package/standalone.py
Running /home/vaultah/project/package/standalone.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>

Hinweis : Wenn Sie sich für diesen Weg entscheiden, sollten Sie virtuelle Umgebungen verwenden , um Pakete isoliert zu installieren.

Lösung 4: Verwenden Sie absolute Importe und etwas Boilerplate-Code

Ehrlich gesagt ist die Installation nicht erforderlich - Sie können Ihrem Skript Boilerplate-Code hinzufügen, damit absolute Importe funktionieren.

Ich werde Dateien von Lösung 1 ausleihen und standalone.py ändern :

  1. Fügen Sie das übergeordnete Verzeichnis des Pakets hinzu , sys.path bevor Sie versuchen, mithilfe absoluter Importe etwas aus dem Paket zu importieren:

    import sys
    from pathlib import Path # if you haven't already done so
    file = Path(__file__).resolve()
    parent, root = file.parent, file.parents[1]
    sys.path.append(str(root))
    
    # Additionally remove the current file's directory from sys.path
    try:
        sys.path.remove(str(parent))
    except ValueError: # Already removed
        pass
  2. Ersetzen Sie den relativen Import durch den absoluten Import:

    from package import module  # absolute import

standalone.py läuft ohne Probleme:

vaultah@base:~$ python3 -i package/standalone.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>

Ich bin der Meinung, dass ich Sie warnen sollte: Versuchen Sie, dies nicht zu tun, insbesondere wenn Ihr Projekt eine komplexe Struktur aufweist.


Als Randnotiz empfiehlt PEP 8 die Verwendung absoluter Importe, gibt jedoch an, dass in einigen Szenarien explizite relative Importe akzeptabel sind:

Absolute Importe werden empfohlen, da sie normalerweise besser lesbar sind und sich tendenziell besser verhalten (oder zumindest bessere Fehlermeldungen liefern). [...] Explizite relative Importe sind jedoch eine akzeptable Alternative zu absoluten Importen, insbesondere bei komplexen Paketlayouts, bei denen die Verwendung absoluter Importe unnötig ausführlich wäre.

Vaultah
quelle
3
Ist es möglich, __package__manuell festzulegen, ob der Name lautet __main__, um das Problem zu lösen?
Paulo Scardine
Danke, schöne Antworten! Ich konnte das Modul mit dem impModul laden und entsprechend einstellen, __package__aber das Ergebnis ist eindeutig ein Anti-Pattern.
Paulo Scardine
Ich bekomme den Fehler AttributeError: 'PosixPath' object has no attribute 'path'.
Benutzer
Danke für die schnelle Antwort. Ich verwende das nltk-Paket und erhalte die Fehlermeldung "Datei" /usr/local/lib/python3.5/dist-packages/nltk/__init__.py ", Zeile 115, in <module> von nltk.decorators Dekorator importieren, Datei "/usr/local/lib/python3.5/dist-packages/nltk/decorators.py", Zeile 23, in <module> sys.path = [p für p in sys.path wenn "nltk "nicht in p] Datei" /usr/local/lib/python3.5/dist-packages/nltk/decorators.py ", Zeile 23, in <listcomp> sys.path = [p für p in sys.path wenn" nltk "nicht in p] TypeError: Argument vom Typ 'PosixPath' ist nicht iterierbar`
Benutzer
1
Sie können eine Datei auch nach Dateipfad importieren (auch relativ): docs.python.org/3/library/…
Strg-C
85

Fügen Sie dies in die Datei __init__.py Ihres Pakets ein :

# For relative imports to work in Python 3.6
import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))

Angenommen, Ihr Paket sieht folgendermaßen aus:

├── project
   ├── package
      ├── __init__.py
      ├── module1.py
      └── module2.py
   └── setup.py

Verwenden Sie jetzt regelmäßige Importe in Ihrem Paket, wie:

# in module2.py
from module1 import class1

Dies funktioniert sowohl in Python 2 als auch in Python 3.

am5
quelle
1
funktioniert das, wenn wir es als weel verpacken
Alex Punnen
1
Ich denke auch, dass dies mehr Stimmen verdient. Wenn Sie dies in jedes eingeben, werden im __init__.pyGrunde alle relativen Importfehler behoben.
Frankliuao
3
Ich kann nicht für andere sprechen, aber ich vermeide Änderungen, sys.pathweil ich befürchte, dass dies Auswirkungen auf anderen Code haben könnte. (Teilweise liegt das daran, dass ich nicht weiß, wie
kompliziert
@pianoJames Ich weiß, was du meinst, diese (scheinbar nach vielem Durchdrehen) magische Lösung scheint ein wenig zu einfach. Aber es funktioniert. Wäre interessiert, nicht von denen zu wissen, die wissen, ob dies negative Nebenwirkungen hat.
Jon
Ich benutze das jetzt: und soweit so gut.
Javadba
37

Ich bin auf dieses Problem gestoßen. Eine Hack-Problemumgehung wird wie folgt über einen if / else-Block importiert:

#!/usr/bin/env python3
#myothermodule

if __name__ == '__main__':
    from mymodule import as_int
else:
    from .mymodule import as_int


# Exported function
def add(a, b):
    return as_int(a) + as_int(b)

# Test function for module  
def _test():
    assert add('1', '1') == 2

if __name__ == '__main__':
    _test()
goffer
quelle
29
Das ist keine sehr schöne Lösung. Auch nackt except:ist schlecht. verwenden except ImportError:statt!
ThiefMaster
6
Es ist SystemErrorhier. (Py 3.4)
Avi
8
Dies ist keine schreckliche Idee, aber es wäre besser zu erkennen, welcher Import verwendet werden soll, als zu versuchen / auszunehmen. So etwas wie if __name__ == '__main__': from mymod import as_int; else: from .mymod import as_int.
Perkins
@Perkins Nun ... in den meisten Fällen würde es nicht . Ich denke jedoch, dass relative Importe die Ausnahme sein können.
wizzwizz4
8

Hoffentlich ist dies für jemanden da draußen von Wert - ich habe ein halbes Dutzend Stackoverflow-Posts durchgesehen, um relative Importe zu ermitteln, die denen ähneln, die hier oben veröffentlicht wurden. Ich habe alles wie vorgeschlagen eingerichtet, aber ich habe immer noch geschlagenModuleNotFoundError: No module named 'my_module_name'

Da ich mich nur lokal entwickelte und herumspielte, hatte ich keine setup.pyDatei erstellt / ausgeführt . Ich hatte anscheinend auch meine nicht eingestellt PYTHONPATH.

Ich stellte fest, dass ich mein Modul nicht finden konnte, als ich meinen Code so ausführte, wie ich es war, als sich die Tests im selben Verzeichnis wie das Modul befanden:

$ python3 test/my_module/module_test.py                                                                                                               2.4.0
Traceback (most recent call last):
  File "test/my_module/module_test.py", line 6, in <module>
    from my_module.module import *
ModuleNotFoundError: No module named 'my_module'

Als ich jedoch den Pfad explizit spezifizierte, begannen die Dinge zu funktionieren:

$ PYTHONPATH=. python3 test/my_module/module_test.py                                                                                                  2.4.0
...........
----------------------------------------------------------------------
Ran 11 tests in 0.001s

OK

Für den Fall, dass jemand ein paar Vorschläge ausprobiert hat, glaubt, dass sein Code korrekt strukturiert ist und sich dennoch in einer ähnlichen Situation befindet wie ich, versuchen Sie eine der folgenden Möglichkeiten, wenn Sie das aktuelle Verzeichnis nicht in Ihren PYTHONPATH exportieren:

  1. Führen Sie Ihren Code aus und geben Sie den Pfad explizit wie folgt an: $ PYTHONPATH=. python3 test/my_module/module_test.py
  2. Um einen Aufruf zu vermeiden PYTHONPATH=., erstellen Sie eine setup.pyDatei mit Inhalten wie den folgenden und führen Sie sie aus python setup.py development, um dem Pfad Pakete hinzuzufügen:
# setup.py
from setuptools import setup, find_packages

setup(
    name='sample',
    packages=find_packages()
)
LaCroixed
quelle
6

Ich musste python3 aus dem Hauptprojektverzeichnis ausführen, damit es funktioniert.

Zum Beispiel, wenn das Projekt die folgende Struktur hat:

project_demo/
├── main.py
├── some_package/
   ├── __init__.py
   └── project_configs.py
└── test/
    └── test_project_configs.py

Lösung

Ich würde python3 im Ordner project_demo / ausführen und dann a ausführen

from some_package import project_configs
Árthur
quelle
4

Um dieses Problem zu vermeiden, habe ich eine Lösung mit dem Umpackpaket entwickelt , die seit einiger Zeit für mich funktioniert. Es fügt das obere Verzeichnis dem lib-Pfad hinzu:

import repackage
repackage.up()
from mypackage.mymodule import myfunction

Durch das Neuverpacken können mithilfe einer intelligenten Strategie (Überprüfung des Aufrufstapels) relative Importe durchgeführt werden, die in einer Vielzahl von Fällen funktionieren.

fralau
quelle
Mit Abstand die einfachste Lösung! Vielen Dank!
CodingInCircles
1
Vielen Dank! Anstatt zu versuchen, die beste Antwort zu geben, habe ich versucht, eine brauchbare Antwort zu geben :-)
fralau
3

Wenn sich beide Pakete in Ihrem Importpfad (sys.path) befinden und sich das gewünschte Modul / die gewünschte Klasse in example / example.py befindet, versuchen Sie, ohne relativen Import auf die Klasse zuzugreifen:

from example.example import fkt
Salt999
quelle
1

Ich denke, die beste Lösung besteht darin, ein Paket für Ihr Modul zu erstellen: Hier finden Sie weitere Informationen dazu.

Sobald Sie ein Paket haben, müssen Sie sich nicht mehr um den relativen Import kümmern, sondern können nur noch absolute Importe durchführen.

Taras Kucherenko
quelle
0

Ich hatte ein ähnliches Problem: Ich brauchte einen Linux-Dienst und ein CGI-Plugin, die gemeinsame Konstanten für die Zusammenarbeit verwenden. Der 'natürliche' Weg, dies zu tun, besteht darin, sie in der init .py des Pakets zu platzieren, aber ich kann das cgi-Plugin nicht mit dem Parameter -m starten.

Meine endgültige Lösung ähnelte der obigen Lösung Nr. 2:

import sys
import pathlib as p
import importlib

pp = p.Path(sys.argv[0])
pack = pp.resolve().parent

pkg = importlib.import_module('__init__', package=str(pack))

Der Nachteil ist, dass Sie den Konstanten (oder allgemeinen Funktionen) pkg voranstellen müssen:

print(pkg.Glob)
Thomas Heckmann
quelle