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

Manchmal funktioniert es bei mir mit from .mymodule import myfunction, aber 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 ein:

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?

645
John Smith Optional 7 Juni 2013 im 14:26

8 Antworten

Beste Antwort

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 einem mymodule.py wie diesem ...

#!/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()

... ein myothermodule.py wie dieses ...

#!/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 ein main.py wie dieses ...

#!/usr/bin/env python3

from mypackage.myothermodule import add

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

if __name__ == '__main__':
    main()

... was gut funktioniert, wenn Sie main.py oder mypackage/mymodule.py ausführen, aber mit mypackage/myothermodule.py aufgrund 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, wenn der Name mymodule global eindeutig ist, 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, das Ihr Paketverzeichnis enthält, in PYTHONPATH aufnehmen müssen, 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 zuerst den PYTHONPATH im Code frobieren ...

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 an E-Mail geschrieben von einem bestimmten Guido van Rossum ...

Ich bin -1 in diesem und in allen anderen vorgeschlagenen Twiddlings des __main__ Maschinen. Der einzige Anwendungsfall scheint das Ausführen von Skripten zu sein, die passieren im Verzeichnis eines Moduls zu leben, das ich immer als gesehen habe Antimuster. Um mich dazu zu bringen, meine Meinung zu ändern, müsstest du mich davon überzeugen es ist nicht.

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 ein {{anzuzeigen X0}} enthält nur dieses Widget zu Testzwecken.

480
Aya 7 Juni 2013 im 13:46

Um dieses Problem zu vermeiden, habe ich eine Lösung mit dem Repackage -Paket entwickelt, für das funktioniert hat ich für einige Zeit. 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.

3
fralau 6 Dez. 2017 im 09:42

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.

68
Brian Burns 16 Mai 2018 im 12:38

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()
32
Leon 13 Jän. 2018 im 19:22

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 oben hier veröffentlicht wurden. Ich habe alles wie vorgeschlagen eingerichtet, aber ich habe immer noch ModuleNotFoundError: No module named 'my_module_name' getroffen

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

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 zu vermeiden, dass PYTHONPATH=. aufgerufen wird, erstellen Sie eine setup.py Datei mit Inhalten wie den folgenden und führen Sie python setup.py development aus, um dem Pfad Pakete hinzuzufügen:
# setup.py
from setuptools import setup, find_packages

setup(
    name='sample',
    packages=find_packages()
)
8
LaCroixed 17 Apr. 2018 im 19:40

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.

0
Taras Kucherenko 17 Okt. 2019 im 09:16

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
1
Salt999 9 März 2017 im 22:18

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
2
Árthur 12 Feb. 2019 im 19:18