Relative Importe zum milliardsten Mal

716

Ich war hier:

und viele URLs, die ich nicht kopiert habe, einige auf SO, andere auf anderen Websites, als ich dachte, ich hätte die Lösung schnell.

Die immer wiederkehrende Frage lautet: Wie löse ich unter Windows 7, 32-Bit-Python 2.7.3, die Meldung "Versuchter relativer Import in Nicht-Paket"? Ich habe eine genaue Replik des Pakets auf pep-0328 erstellt:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
        moduleY.py
    subpackage2/
        __init__.py
        moduleZ.py
    moduleA.py

Die Importe wurden von der Konsole aus durchgeführt.

Ich habe Funktionen namens Spam und Eier in den entsprechenden Modulen erstellt. Natürlich hat es nicht funktioniert. Die Antwort ist anscheinend in der 4. URL, die ich aufgelistet habe, aber es sind alles Alumni für mich. Es gab diese Antwort auf eine der URLs, die ich besucht habe:

Relative Importe verwenden das Namensattribut 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.

Die obige Antwort sieht vielversprechend aus, aber für mich sind alles Hieroglyphen. Also meine Frage, wie kann ich Python dazu bringen, nicht zu mir zurückzukehren "Versuchter relativer Import in Nicht-Paket"? hat eine Antwort, die angeblich -m beinhaltet.

Kann mir bitte jemand sagen, warum Python diese Fehlermeldung gibt, was es unter "Nicht-Paket" versteht, warum und wie Sie ein "Paket" definieren und die genaue Antwort so formuliert ist, dass ein Kindergärtner sie leicht verstehen kann .

Paul Roub
quelle
5
Wie versuchen Sie, die angezeigten Dateien zu verwenden? Was ist der Code, den Sie ausführen?
BrenBarn
Siehe python.org/dev/peps/pep-0328 . Ich habe das Paketformat verwendet, das ich in meinem Beitrag beschrieben habe. Die init .py-Dateien sind leer. moduleY.py hat def spam(): pass, moduleA.py hat def eggs(): pass. Ich habe versucht, ein paar Befehle "von etwas importieren" auszuführen, aber sie haben nicht funktioniert. Siehe auch pep-0328.
6
Siehe meine Antwort. Sie haben noch nicht vollständig geklärt, was Sie tun, aber wenn Sie versuchen, dies from .something import somethingim interaktiven Interpreter zu tun , funktioniert dies nicht. Relative Importe können nur innerhalb von Modulen verwendet werden, nicht interaktiv.
BrenBarn
105
Die bloße Tatsache, dass "Milliarden" von Menschen - ok 83.136 ab diesem Kommentar - genug Schwierigkeiten mit Importen haben, um diese Frage zu suchen; Wir können nur den Schluss ziehen, dass Python-Importe für viele, wenn nicht die meisten Programmierer, nicht intuitiv sind. Guido, vielleicht solltest du das akzeptieren und um ein Komitee bitten, das den Importmechanismus neu gestaltet. Zumindest sollte diese Syntax funktionieren, wenn sich x.py und z.py im selben Verzeichnis befinden. Nämlich wenn x.py die Anweisung "aus .z MyZebraClass importieren" hat, sollte x z AUCH importieren, wenn es als main ausgeführt wird ! Warum ist das so schwer?
Steve L
4
Nach dem Lesen eines Großteils dieses Threads, obwohl keine Antwort auf die Frage, scheint "nur absolute Importe verwenden" die Lösung zu sein ...
CodeJockey

Antworten:

1042

Skript vs. Modul

Hier ist eine Erklärung. Die Kurzversion ist, dass es einen großen Unterschied zwischen dem direkten Ausführen einer Python-Datei und dem Importieren dieser Datei von einem anderen Ort gibt. Nur zu wissen, in welchem ​​Verzeichnis sich eine Datei befindet, bestimmt nicht, in welchem ​​Paket Python sich befindet. Dies hängt außerdem davon ab, wie Sie die Datei in Python laden (durch Ausführen oder Importieren).

Es gibt zwei Möglichkeiten, eine Python-Datei zu laden: als Skript der obersten Ebene oder als Modul. Eine Datei wird als Skript der obersten Ebene geladen, wenn Sie sie direkt ausführen, z. B. durch Eingabe python myfile.pyin der Befehlszeile. Es wird als Modul geladen, wenn Sie dies tun python -m myfile, oder wenn es geladen wird, wenn eine importAnweisung in einer anderen Datei gefunden wird. Es kann immer nur ein Skript der obersten Ebene vorhanden sein. Das Skript der obersten Ebene ist die Python-Datei, die Sie ausgeführt haben, um die Dinge zu starten.

Benennung

Wenn eine Datei geladen wird, erhält sie einen Namen (der in ihrem __name__Attribut gespeichert ist ). Wenn es als Skript der obersten Ebene geladen wurde, lautet sein Name __main__. Wenn es als Modul geladen wurde, ist sein Name der Dateiname, vor dem die Namen aller Pakete / Unterpakete stehen, zu denen es gehört, getrennt durch Punkte.

Also zum Beispiel in Ihrem Beispiel:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

Wenn Sie importiert haben moduleX(Hinweis: importiert , nicht direkt ausgeführt), lautet der Name package.subpackage1.moduleX. Wenn Sie importieren moduleA, wäre der Name package.moduleA. Wenn Sie jedoch direkt moduleX über die Befehlszeile ausgeführt werden, lautet der Name stattdessen __main__, und wenn Sie direkt moduleAüber die Befehlszeile ausgeführt werden, lautet der Name __main__. Wenn ein Modul als Skript der obersten Ebene ausgeführt wird, verliert es seinen normalen Namen und stattdessen seinen Namen __main__.

Zugriff auf ein Modul NICHT über das enthaltene Paket

Es gibt eine zusätzliche Falte: Der Name des Moduls hängt davon ab, ob es "direkt" aus dem Verzeichnis, in dem es sich befindet, oder über ein Paket importiert wurde. Dies macht nur dann einen Unterschied, wenn Sie Python in einem Verzeichnis ausführen und versuchen, eine Datei in dasselbe Verzeichnis (oder ein Unterverzeichnis davon) zu importieren. Wenn Sie beispielsweise den Python-Interpreter im Verzeichnis starten package/subpackage1und dies dann tun import moduleX, lautet der Name moduleXnur moduleXund nicht package.subpackage1.moduleX. Dies liegt daran, dass Python beim Start das aktuelle Verzeichnis zu seinem Suchpfad hinzufügt. Wenn das zu importierende Modul im aktuellen Verzeichnis gefunden wird, weiß es nicht, dass dieses Verzeichnis Teil eines Pakets ist, und die Paketinformationen werden nicht Teil des Modulnamens.

Ein Sonderfall ist, wenn Sie den Interpreter interaktiv ausführen (z. B. einfach pythonPython-Code eingeben und sofort eingeben). In diesem Fall lautet der Name dieser interaktiven Sitzung __main__.

Hier ist das Entscheidende für Ihre Fehlermeldung: Wenn der Name eines Moduls keine Punkte enthält, wird er nicht als Teil eines Pakets betrachtet . Es spielt keine Rolle, wo sich die Datei tatsächlich auf der Festplatte befindet. Alles, was zählt, ist der Name und der Name hängt davon ab, wie Sie ihn geladen haben.

Schauen Sie sich nun das Zitat an, das Sie in Ihre Frage aufgenommen haben:

Relative Importe verwenden das Namensattribut 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.

Relative Importe ...

Relative Importe verwenden den Namen des Moduls, um zu bestimmen, wo es sich in einem Paket befindet. Wenn Sie einen relativen Import wie verwenden from .. import foo, geben die Punkte an, dass einige Ebenen in der Pakethierarchie erhöht werden sollen. Wenn der Name Ihres aktuellen Moduls beispielsweise lautet package.subpackage1.moduleX, ..moduleAwürde dies bedeuten package.moduleA. Damit ein from .. importModul funktioniert, muss der Name des Moduls mindestens so viele Punkte enthalten, wie in der importAnweisung enthalten sind.

... sind nur in einem Paket relativ

Wenn der Name Ihres Moduls jedoch lautet __main__, wird er nicht als in einem Paket enthalten betrachtet. Sein Name hat keine Punkte und daher können Sie keine darin enthaltenen from .. importAnweisungen verwenden. Wenn Sie dies versuchen, wird der Fehler "Relativer Import in Nicht-Paket" angezeigt.

Skripte können nicht relativ importiert werden

Was Sie wahrscheinlich getan haben, ist, dass Sie versucht haben, moduleXüber die Befehlszeile oder ähnliches auszuführen . Als Sie dies getan haben, wurde sein Name auf gesetzt __main__, was bedeutet, dass relative Importe darin fehlschlagen, da sein Name nicht anzeigt, dass es sich in einem Paket befindet. Beachten Sie, dass dies auch passiert, wenn Sie Python aus demselben Verzeichnis ausführen, in dem sich ein Modul befindet, und dann versuchen, dieses Modul zu importieren, da Python das Modul wie oben beschrieben "zu früh" im aktuellen Verzeichnis findet, ohne es zu bemerken Teil eines Pakets.

Denken Sie auch daran, dass beim Ausführen des interaktiven Interpreters immer der "Name" dieser interaktiven Sitzung lautet __main__. Daher können Sie keine relativen Importe direkt aus einer interaktiven Sitzung durchführen . Relative Importe sind nur zur Verwendung in Moduldateien vorgesehen.

Zwei Lösungen:

  1. Wenn Sie wirklich moduleXdirekt ausgeführt werden möchten, es aber dennoch als Teil eines Pakets betrachtet werden sollen, können Sie dies tun python -m package.subpackage1.moduleX. Das -mweist Python an, es als Modul zu laden, nicht als Skript der obersten Ebene.

  2. Oder vielleicht möchten Sie nicht wirklich ausführen moduleX , sondern nur ein anderes Skript ausführen myfile.py, das beispielsweise Funktionen verwendetmoduleX . Wenn dies der Fall ist, legen Sie es an einer myfile.py anderen Stelle ab - nicht im packageVerzeichnis - und führen Sie es aus. Wenn myfile.pySie Dinge wie tun from package.moduleA import spam, wird es gut funktionieren.

Anmerkungen

  • Für jede dieser Lösungen muss auf das Paketverzeichnis ( packagein Ihrem Beispiel) über den Suchpfad des Python-Moduls ( sys.path) zugegriffen werden können . Ist dies nicht der Fall, können Sie nichts im Paket zuverlässig verwenden.

  • Seit Python 2.6 wird der "Name" des Moduls für Zwecke der Paketauflösung nicht nur durch seine __name__Attribute, sondern auch durch das __package__Attribut bestimmt. Deshalb vermeide ich es, das explizite Symbol __name__zu verwenden, um auf den "Namen" des Moduls zu verweisen. Da Python 2.6 ein Modul der „name“ ist effektiv __package__ + '.' + __name__, oder einfach nur , __name__wenn __package__ist None.)

BrenBarn
quelle
62
Its name has no dots, and therefore you cannot use from .. import statements inside it. If you try to do so, you will get the "relative-import in non-package" error.Das ist grundsätzlich beunruhigend. Was ist so schwierig am Betrachten des aktuellen Verzeichnisses? Python sollte dazu in der Lage sein. Ist das in Version 3x behoben?
7
@Stopforgettingmyaccounts ...: PEP 366 zeigt, wie das funktioniert. Innerhalb einer Datei können Sie __package__ = 'package.subpackage1'oder dergleichen tun . Dann nur die Datei wird immer ein Teil dieses Paket betrachtet werden , auch wenn direkt ausgeführt werden . Wenn Sie weitere Fragen zu haben __package__, möchten Sie möglicherweise eine separate Frage stellen, da wir hier das Problem Ihrer ursprünglichen Frage lösen.
BrenBarn
108
Dies sollte die Antwort auf alle Fragen zu Python-relativen Importen sein. Dies sollte sogar in den Dokumenten enthalten sein.
Edsioufi
10
Siehe python.org/dev/peps/pep-0366 - "Beachten Sie, dass dieses Boilerplate nur dann ausreicht, wenn auf das Paket der obersten Ebene bereits über sys.path zugegriffen werden kann. Für die direkte Ausführung wäre zusätzlicher Code erforderlich, der sys.path manipuliert zu arbeiten, ohne dass das Top-Level-Paket bereits importierbar ist. " - Dies ist das beunruhigendste für mich, da dieser "zusätzliche Code" eigentlich ziemlich lang ist und nicht an anderer Stelle im Paket gespeichert werden kann, um einfach ausgeführt zu werden.
Michael Scott Cuthbert
14
Diese Antwort enthält derzeit einige wichtige Details zu __name__und sys.path. Genauer gesagt, mit python -m pkg.mod, __name__festgelegt ist __main__, nicht pkg.mod; relative Importe werden __package__nicht __name__in diesem Fall , sondern mit aufgelöst . Außerdem fügt Python sys.pathbeim Ausführen das Verzeichnis des Skripts und nicht das aktuelle Verzeichnis hinzu python path/to/script.py. Es fügt das aktuelle Verzeichnis hinzu, sys.pathwenn die meisten anderen Methoden ausgeführt werden, einschließlich python -m pkg.mod.
user2357112 unterstützt Monica
42

Dies ist wirklich ein Problem in Python. Der Grund für die Verwirrung ist, dass Menschen fälschlicherweise den relativen Import als relativen Pfad betrachten, was nicht der Fall ist.

Zum Beispiel, wenn Sie in faa.py schreiben :

from .. import foo

Dies hat nur einen Sinn , wenn faa.py wurde identifiziert und geladen von Python, die während der Ausführung, als Teil eines Pakets. In diesem Fall wäre der Name des Moduls für faa.py beispielsweise some_packagename.faa . Wenn die Datei geladen wurde, nur weil sie sich im aktuellen Verzeichnis befindet, wenn Python ausgeführt wird, verweist ihr Name nicht auf ein Paket und der relative Import schlägt möglicherweise fehl.

Eine einfache Lösung zum Verweisen auf Module im aktuellen Verzeichnis besteht darin, Folgendes zu verwenden:

if __package__ is None or __package__ == '':
    # uses current directory visibility
    import foo
else:
    # uses current package visibility
    from . import foo
Rami Ka.
quelle
6
Die richtige Lösung ist from __future__ import absolute_importund zwingen Sie den Benutzer, Ihren Code korrekt zu verwenden ... so dass Sie immer tun könnenfrom . import foo
Giacomo Alzetta
@ Giacomo: die absolut richtige Antwort für mein Problem. Vielen Dank!
Fábio
8

Hier ist ein allgemeines Rezept, das als Beispiel geändert wurde und das ich derzeit für den Umgang mit Python-Bibliotheken verwende, die als Pakete geschrieben sind und voneinander abhängige Dateien enthalten, in denen ich Teile davon stückweise testen möchte. Nennen wir dies lib.foound sagen wir, dass lib.fileAfür Funktionen f1und f2und lib.fileBfür die Klasse Zugriff erforderlich ist Class3.

Ich habe einige printAufrufe beigefügt , um zu veranschaulichen, wie dies funktioniert. In der Praxis möchten Sie sie (und möglicherweise auch die from __future__ import print_functionLinie) entfernen .

Dieses spezielle Beispiel ist zu einfach, um zu zeigen, wann wir wirklich einen Eintrag in einfügen müssen sys.path. (Siehe Lars 'Antwort für einen Fall, in dem wir es brauchen, wenn wir zwei oder mehr Ebenen von Paketverzeichnissen haben und dann verwenden os.path.dirname(os.path.dirname(__file__))- aber es tut auch hier nicht wirklich weh .) Es ist auch sicher genug, dies ohne das zu tun if _i in sys.pathPrüfung. Wenn jedoch jede importierte Datei denselben Pfad einfügt, z. B. wenn beide fileAund fileBDienstprogramme aus dem Paket importieren möchten, wird sys.pathder Pfad häufig mit demselben Pfad überfüllt , sodass es schön ist, den Pfad if _i not in sys.pathin der Boilerplate zu haben.

from __future__ import print_function # only when showing how this works

if __package__:
    print('Package named {!r}; __name__ is {!r}'.format(__package__, __name__))
    from .fileA import f1, f2
    from .fileB import Class3
else:
    print('Not a package; __name__ is {!r}'.format(__name__))
    # these next steps should be used only with care and if needed
    # (remove the sys.path manipulation for simple cases!)
    import os, sys
    _i = os.path.dirname(os.path.abspath(__file__))
    if _i not in sys.path:
        print('inserting {!r} into sys.path'.format(_i))
        sys.path.insert(0, _i)
    else:
        print('{!r} is already in sys.path'.format(_i))
    del _i # clean up global name space

    from fileA import f1, f2
    from fileB import Class3

... all the code as usual ...

if __name__ == '__main__':
    import doctest, sys
    ret = doctest.testmod()
    sys.exit(0 if ret.failed == 0 else 1)

Die Idee hier ist folgende (und beachten Sie, dass diese alle in Python2.7 und Python 3.x gleich funktionieren):

  1. Wenn es als import liboder from lib import fooals regulärer Paketimport aus normalem Code ausgeführt wird, __packageist libund __name__ist lib.foo. Wir nehmen den ersten Codepfad, importieren aus .fileAusw.

  2. Wenn ausgeführt als python lib/foo.py, __package__wird Keine sein und __name__wird sein __main__.

    Wir nehmen den zweiten Codepfad. Das libVerzeichnis befindet sich bereits, sys.pathsodass es nicht hinzugefügt werden muss. Wir importieren aus fileAusw.

  3. Wenn im libVerzeichnis als ausgeführt python foo.py, ist das Verhalten dasselbe wie in Fall 2.

  4. Bei Ausführung innerhalb des libVerzeichnisses als python -m fooähnelt das Verhalten den Fällen 2 und 3. Der Pfad zum libVerzeichnis befindet sich jedoch nicht in sys.path. Daher fügen wir ihn vor dem Import hinzu. Gleiches gilt, wenn wir Python ausführen und dann import foo.

    (Da . ist in sys.path, wissen wir nicht wirklich brauchen hier die absolute Version des Pfades hinzuzufügen. Dies ist , wo eine tiefes Paket Verschachtelung, wo wir tun wollen from ..otherlib.fileC import ..., einen Unterschied macht. Wenn Sie das nicht tun, können Sie alle sys.pathManipulationen ganz weglassen .)

Anmerkungen

Es gibt immer noch eine Eigenart. Wenn Sie diese ganze Sache von außen ausführen:

$ python2 lib.foo

oder:

$ python3 lib.foo

Das Verhalten hängt vom Inhalt von ab lib/__init__.py. Wenn das existiert und leer ist , ist alles in Ordnung:

Package named 'lib'; __name__ is '__main__'

Aber wenn lib/__init__.py selbst importiert, routineso dass es routine.namedirekt als exportieren kann lib.name, erhalten Sie:

$ python2 lib.foo
Package named 'lib'; __name__ is 'lib.foo'
Package named 'lib'; __name__ is '__main__'

Das heißt, das Modul wird zweimal importiert, einmal über das Paket und dann erneut __main__, damit Ihr mainCode ausgeführt wird. Python 3.6 und höher warnen davor:

$ python3 lib.routine
Package named 'lib'; __name__ is 'lib.foo'
[...]/runpy.py:125: RuntimeWarning: 'lib.foo' found in sys.modules
after import of package 'lib', but prior to execution of 'lib.foo';
this may result in unpredictable behaviour
  warn(RuntimeWarning(msg))
Package named 'lib'; __name__ is '__main__'

Die Warnung ist neu, das Warnverhalten jedoch nicht. Es ist Teil dessen, was manche als Doppelimportfalle bezeichnen . (Weitere Einzelheiten finden Sie in Ausgabe 27487. ) Nick Coghlan sagt:

Diese nächste Falle existiert in allen aktuellen Versionen von Python, einschließlich 3.3, und kann in der folgenden allgemeinen Richtlinie zusammengefasst werden: "Fügen Sie niemals ein Paketverzeichnis oder ein Verzeichnis innerhalb eines Pakets direkt zum Python-Pfad hinzu".

Beachten Sie, dass wir hier zwar gegen diese Regel verstoßen, dies jedoch nur tun, wenn die zu ladende Datei nicht als Teil eines Pakets geladen wird. Unsere Änderung wurde speziell entwickelt, um den Zugriff auf andere Dateien in diesem Paket zu ermöglichen. (Und wie ich bereits erwähnt habe, sollten wir dies für einstufige Pakete wahrscheinlich überhaupt nicht tun.) Wenn wir besonders sauber sein möchten, können wir dies wie folgt umschreiben: z.

    import os, sys
    _i = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    if _i not in sys.path:
        sys.path.insert(0, _i)
    else:
        _i = None

    from sub.fileA import f1, f2
    from sub.fileB import Class3

    if _i:
        sys.path.remove(_i)
    del _i

Das heißt, wir ändern sys.pathlange genug, um unsere Importe zu erzielen, und setzen es dann wieder so ein, wie es war (Löschen einer Kopie von _igenau dann, wenn wir eine Kopie von hinzugefügt haben _i).

torek
quelle
7

Nachdem ich zusammen mit vielen anderen darüber nachgedacht hatte, stieß ich auf eine Notiz von Dorian B in diesem Artikel , die das spezifische Problem löste, das ich hatte, wo ich Module und Klassen für die Verwendung mit einem Webdienst entwickeln würde, aber ich möchte es auch sein Sie können sie beim Codieren mithilfe der Debugger-Funktionen in PyCharm testen. Um Tests in einer eigenständigen Klasse auszuführen, würde ich am Ende meiner Klassendatei Folgendes einfügen:

if __name__ == '__main__':
   # run test code here...

Wenn ich jedoch andere Klassen oder Module in denselben Ordner importieren wollte, musste ich alle meine Importanweisungen von der relativen Notation in lokale Referenzen ändern (dh den Punkt (.) entfernen). Nachdem ich Dorians Vorschlag gelesen hatte, versuchte ich es mit seinem ' Einzeiler 'und es hat funktioniert! Ich kann jetzt in PyCharm testen und meinen Testcode beibehalten, wenn ich die Klasse in einer anderen getesteten Klasse verwende oder wenn ich sie in meinem Webdienst verwende!

# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __name__ == '__main__' or parent_module.__name__ == '__main__':
    from codex import Codex # these are in same folder as module under test!
    from dblogger import DbLogger
else:
    from .codex import Codex
    from .dblogger import DbLogger

Die if-Anweisung prüft, ob dieses Modul als Hauptmodul ausgeführt wird oder ob es in einem anderen Modul verwendet wird, das als Hauptmodul getestet wird . Vielleicht ist dies offensichtlich, aber ich biete diesen Hinweis hier an, falls jemand anderes, der von den oben genannten relativen Importproblemen frustriert ist, davon Gebrauch machen kann.

Steve L.
quelle
1
Das löst es tatsächlich. Aber es ist wirklich böse. Warum ist dies nicht das Standardverhalten?!
Siehe
4

Hier ist eine Lösung, die ich nicht empfehlen würde, die aber in einigen Situationen nützlich sein könnte, in denen Module einfach nicht generiert wurden:

import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/your_dir")
import your_script
your_script.a_function()
Federico
quelle
2

Ich hatte ein ähnliches Problem, bei dem ich den Suchpfad des Python-Moduls nicht ändern wollte und ein Modul relativ aus einem Skript laden musste (trotz "Skripte können nicht relativ mit allen importiert werden"). wie BrenBarn oben ausführlich erläutert hat).

Also habe ich den folgenden Hack benutzt. Leider ist es darauf angewiesen, impdass das Modul, das seit Version 3.4 veraltet ist, zugunsten von gelöscht wird importlib. (Ist das auch mit möglich importlib? Ich weiß es nicht.) Trotzdem funktioniert der Hack vorerst.

Beispiel für den Zugriff auf Mitglieder von moduleXin subpackage1über ein Skript im subpackage2Ordner:

#!/usr/bin/env python3

import inspect
import imp
import os

def get_script_dir(follow_symlinks=True):
    """
    Return directory of code defining this very function.
    Should work from a module as well as from a script.
    """
    script_path = inspect.getabsfile(get_script_dir)
    if follow_symlinks:
        script_path = os.path.realpath(script_path)
    return os.path.dirname(script_path)

# loading the module (hack, relying on deprecated imp-module)
PARENT_PATH = os.path.dirname(get_script_dir())
(x_file, x_path, x_desc) = imp.find_module('moduleX', [PARENT_PATH+'/'+'subpackage1'])
module_x = imp.load_module('subpackage1.moduleX', x_file, x_path, x_desc)

# importing a function and a value
function = module_x.my_function
VALUE = module_x.MY_CONST

Ein sauberer Ansatz scheint darin zu bestehen, den von Federico erwähnten sys.path zum Laden von Modulen zu ändern.

#!/usr/bin/env python3

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    # __file__ should be defined in this case
    PARENT_DIR = path.dirname(path.dirname(path.abspath(__file__)))
   sys.path.append(PARENT_DIR)
from subpackage1.moduleX import *
Lars
quelle
Das sieht besser aus ... schade, dass Sie immer noch den Namen des übergeordneten Verzeichnisses in die Datei einbetten müssen ... vielleicht kann das mit importlib verbessert werden. Möglicherweise kann importlib sogar monkeypatched werden, damit der relative Import für einfache Anwendungsfälle "nur funktioniert". Ich werde es versuchen.
Andrew Wagner
Ich verwende jedoch Python 2.7.14. Würde so etwas noch funktionieren?
user3474042
Ich habe gerade beide Ansätze auf Python 2.7.10 getestet und sie haben für mich gut funktioniert. Tatsächlich haben Sie in 2.7 nicht das Problem eines veralteten imp-Moduls, also umso besser.
Lars
2

__name__ ändert sich abhängig davon, ob der betreffende Code im globalen Namespace oder als Teil eines importierten Moduls ausgeführt wird.

Wenn der Code nicht im globalen Bereich ausgeführt __name__wird, lautet der Name des Moduls. Wenn im globalen Namensraum läuft - zum Beispiel, wenn Sie es in eine Konsole eingeben oder das Modul als Skript ausgeführt unter Verwendung python.exe yourscriptnamehere.pydann __name__wird "__main__".

Sie werden sehen, dass viel Python-Code if __name__ == '__main__'verwendet wird, um zu testen, ob der Code aus dem globalen Namespace ausgeführt wird. Auf diese Weise können Sie ein Modul verwenden, das gleichzeitig als Skript fungiert.

Haben Sie versucht, diese Importe von der Konsole durchzuführen?

theodox
quelle
Ah, also erwähnen Sie -m. Dadurch wird Ihr Modul als Skript ausgeführt. Wenn Sie ein if __name__ == '__main__' einfügen, sollten Sie sehen, dass es aufgrund des -m '__main__' ist. Versuchen Sie einfach, Ihr Modul in ein anderes Modul zu importieren, damit es nicht die oberste Ebene ist ... die es Ihnen ermöglichen sollte, den relativen Import
durchzuführen
Ich habe versucht, diese Importe von der Konsole durchzuführen, wobei die aktive Datei das richtige Modul ist.
@Stopforgettingmyaccounts ...: Was meinst du mit der "aktiven Datei"?
BrenBarn
Ich benutze Pyscripter. Ich war in moduleX.py, als ich diese Importe ausführte: von .moduleY Import-Spam und von. ModulY importieren.
Nicht .moduleY importieren, gefolgt von moduleY.spam ()?
Theodox
2

@ BrenBarns Antwort sagt alles, aber wenn du wie ich bist, kann es eine Weile dauern, bis du es verstehst. Hier ist mein Fall und wie die Antwort von @ BrenBarn darauf zutrifft, vielleicht hilft es Ihnen.

Der Fall

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

Verwenden Sie unser bekanntes Beispiel und fügen Sie hinzu, dass moduleX.py einen relativen Import zu ..moduleA hat. Angesichts der Tatsache, dass ich versucht habe, ein Testskript in das Verzeichnis subpackage1 zu schreiben, das moduleX importiert hat, aber dann den vom OP beschriebenen gefürchteten Fehler erhalten habe.

Lösung

Verschieben Sie das Testskript auf die gleiche Ebene wie package und importieren Sie package.subpackage1.moduleX

Erläuterung

Wie erläutert, werden relative Importe relativ zum aktuellen Namen durchgeführt. Wenn mein Testskript moduleX aus demselben Verzeichnis importiert, lautet der Modulname in moduleX moduleX. Wenn ein relativer Import auftritt, kann der Interpreter die Pakethierarchie nicht sichern, da sie sich bereits oben befindet

Wenn ich moduleX von oben importiere, lautet der Name in moduleX package.subpackage1.moduleX und der relative Import kann gefunden werden

Brad Dre
quelle
Ich hoffe, Sie können mich dabei unterstützen. Wenn Sie im folgenden Link zu Fall 3 gehen, heißt es, dass Lösung 1 nicht möglich ist. Bitte können Sie dies überprüfen und mich wissen lassen. Es wird mir sehr helfen. chrisyeh96.github.io/2017/08/08/…
Variable
@variable Der Link enthält einen Tippfehler und ich darf ihn nicht bearbeiten. Sah Fall 3 an und folgte nicht genau dem, worauf Sie hinaus wollen. Als ich dieses Beispiel in Python 2 ausprobierte, gab es keine Probleme, die mich glauben ließen, ich hätte etwas verpasst. Vielleicht sollten Sie eine neue Frage stellen, müssen aber ein klareres Beispiel liefern. Fall 4 berührt das, worüber ich in meiner Antwort hier spreche: Sie können kein Verzeichnis für den relativen Import aufrufen, es sei denn, der Interpreter startet in einem übergeordneten Verzeichnis
Brad Dre
Vielen Dank, ich beziehe mich auf Python 3 und hier die Frage stackoverflow.com/questions/58577767/…
Variable
1

Relative Importe verwenden das Namensattribut 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.

Schrieb ein kleines Python-Paket an PyPi, das den Zuschauern dieser Frage helfen könnte. Das Paket dient als Problemumgehung, wenn Python-Dateien mit Importen, die Pakete der oberen Ebene enthalten, aus einem Paket / Projekt ausgeführt werden sollen, ohne sich direkt im Verzeichnis der importierenden Datei zu befinden. https://pypi.org/project/import-anywhere/

ec2604
quelle
-2

Damit Python nicht zu mir zurückkehrt "Versuchter relativer Import in Nicht-Paket". Paket/

init .py subpackage1 / init .py moduleX.py moduleY.py subpackage2 / init .py moduleZ.py moduleA.py

Dieser Fehler tritt nur auf, wenn Sie einen relativen Import auf die übergeordnete Datei anwenden. Zum Beispiel gibt die übergeordnete Datei bereits main zurück, nachdem Sie "print ( name )" in moduleA.py codiert haben. Diese Datei ist also bereits vorhanden mainEs kann kein übergeordnetes Paket weitergeben. Relative Importe sind in Dateien der Pakete subpackage1 und subpackage2 erforderlich. Sie können ".." verwenden, um auf das übergeordnete Verzeichnis oder Modul zu verweisen. Das übergeordnete Verzeichnis ist jedoch, wenn es bereits ein Paket der obersten Ebene ist, kann es nicht weiter über dieses übergeordnete Verzeichnis (Paket) hinausgehen. Solche Dateien, in denen Sie den relativen Import auf Eltern anwenden, können nur mit der Anwendung des absoluten Imports funktionieren. Wenn Sie ABSOLUTE IMPORT IN ELTERNPAKET verwenden, tritt KEIN FEHLER auf, da Python weiß, wer sich auf der obersten Ebene des Pakets befindet, selbst wenn sich Ihre Datei aufgrund des Konzepts von PYTHON PATH, das die oberste Ebene des Projekts definiert, in Unterpaketen befindet

Sakshi Jain
quelle