744 Stimmen

Was kann ich gegen "ImportError: Kann den Namen X nicht importieren" oder "AttributeError: ... (wahrscheinlich aufgrund eines zirkulären Imports)" tun?

Ich habe einige Codefragmente, die sich über mehrere Dateien erstrecken und versuchen, sich gegenseitig zu importieren, wie folgt:

main.py:

from entity import Ent

entity.py:

from physics import Physics
class Ent:
    ...

physics.py:

from entity import Ent
class Physics:
    ...

Dann führe ich main.py aus und erhalte den folgenden Fehler:

Traceback (most recent call last):
File "main.py", line 2, in 
    from entity import Ent
File ".../entity.py", line 5, in 
    from physics import Physics
File ".../physics.py", line 2, in 
    from entity import Ent
ImportError: cannot import name Ent

Ich gehe davon aus, dass der Fehler darauf zurückzuführen ist, dass ich entity zweimal importiere - einmal in main.py und später in physics.py - aber wie kann ich das Problem umgehen?


Siehe auch <a href="https://stackoverflow.com/questions/744373">Was passiert bei der Verwendung von gegenseitigen oder zirkulären Imports in Python?</a> für einen allgemeinen Überblick darüber, was erlaubt ist und was ein Problem bezüglich zirkulärer Imports verursacht. Siehe <a href="https://stackoverflow.com/questions/22187279">Warum scheinen zirkuläre Imports weiter oben im Aufrufstapel zu funktionieren, lösen aber dann einen ImportError weiter unten aus?</a> für technische Details darüber, <strong>warum und wie</strong> das Problem auftritt.

1 Stimmen

@jsells Du solltest deine Klassen einfach Entity und Vektor nennen, anstatt Ent und Vect, es gibt keinen Grund, solche Namen zu kürzen. Und ja, benutze import vektor und dann x = vektor.Vektor(0,0,0).

8 Stimmen

Hey @Kevin, da du Java besser kennst, was ist dein Eindruck von diesem Artikel von 2008, in dem der Autor im ersten Satz darauf hinweist, dass zirkuläre Abhängigkeiten in Java "ziemlich weit verbreitet" sind?

1 Stimmen

Jede wichtige Sprache unterstützt zirkuläre Imports ohne Probleme... C#, C, C++, Java, Objective-C... Wir haben nicht das Jahr 1991.

614voto

Teemu Ikonen Punkte 11549

Sie haben zirkuläre abhängige Imports. physics.py wird von entity importiert, bevor die Klasse Ent definiert ist, und physics versucht, entity zu importieren, das bereits initialisiert wird. Entfernen Sie die Abhängigkeit von physics aus dem Modul entity.

180voto

bharling Punkte 2718

Während Sie definitiv zirkuläre Abhängigkeiten vermeiden sollten, können Sie Importe in Python verzögern.

Zum Beispiel:

import SomeModule

def someFunction(arg):
    from some.dependency import DependentClass

Dies wird (zumindest in einigen Fällen) den Fehler umgehen.

138voto

Dunes Punkte 34391

Dies ist eine zirkuläre Abhängigkeit. Es kann ohne strukturelle Änderungen am Code gelöst werden. Das Problem tritt auf, weil in vector gefordert wird, dass entity sofort zur Verfügung steht und umgekehrt. Der Grund für dieses Problem ist, dass Sie auf die Inhalte des Moduls zugreifen, bevor es bereit ist - indem Sie from x import y verwenden. Das entspricht im Wesentlichen dem Folgenden

import x
y = x.y
del x

Python ist in der Lage, zirkuläre Abhängigkeiten zu erkennen und die Endlosschleife von Importen zu verhindern. Im Grunde wird ein leerer Platzhalter für das Modul erstellt (d.h. es hat keinen Inhalt). Sobald die zirkulär abhängigen Module kompiliert sind, wird das importierte Modul aktualisiert. Das funktioniert so:

a = module() # import a

# Rest des Moduls

a.update_contents(real_a)

Um mit zirkulären Abhängigkeiten in Python arbeiten zu können, müssen Sie nur den import x-Stil verwenden.

import x
class cls:
    def __init__(self):
        self.y = x.y

Weil Sie nicht mehr auf die Inhalte des Moduls auf der obersten Ebene verweisen, kann Python das Modul kompilieren, ohne tatsächlich auf die Inhalte der zirkulären Abhängigkeit zugreifen zu müssen. Mit oberster Ebene meine ich Linien, die während der Kompilierung ausgeführt werden, im Gegensatz zu den Inhalten von Funktionen (z. B. y = x.y). Statische oder Klassenvariablen, die auf die Inhalte des Moduls zugreifen, werden ebenfalls Probleme verursachen.

38voto

Harry M Punkte 1718

In meinem Fall arbeitete ich in einem Jupyter-Notebook und dies passierte, weil der Import bereits im Cache war, als ich die Klasse/Funktion in meiner Arbeitsdatei definiert hatte.

Ich habe meinen Jupyter-Kernel neu gestartet und der Fehler verschwand.

28voto

g10guang Punkte 3583

Es ist sehr wichtig, die Logik klar zu machen. Dieses Problem tritt auf, weil die Referenz zu einer Endlosschleife wird.

Wenn Sie die Logik nicht ändern möchten, können Sie den Importbefehl, der zu ImportError geführt hat, an eine andere Stelle in der Datei setzen, zum Beispiel ans Ende.

a.py

from test.b import b2

def a1():
    print('a1')
    b2()

b.py

from test.a import a1

def b1():
    print('b1')
    a1()

def b2():
    print('b2')

if __name__ == '__main__':
    b1()

Sie erhalten den Importfehler: ImportError: kann den Namen 'a1' nicht importieren

Aber wenn wir die Position von `from test.b import b2` in A wie folgt ändern:

a.py

def a1():
    print('a1')
    b2()

from test.b import b2

Dann können wir bekommen, was wir wollen:

b1
a1
b2

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X