Python-Zirkularimport

Salman Mehmood 15 Februar 2024
  1. Zirkulärer Importfehler in Python reproduzieren
  2. Beheben Sie den zirkulären Importfehler in Python
  3. Beheben Sie den zirkulären Importfehler, der durch den Typhinweis in Python verursacht wurde
Python-Zirkularimport

In diesem Artikel erfahren Sie, wie zirkuläre Importe häufig entstehen und wie Sie sie beheben können. Wir werden auch sehen, wie ein zirkulärer Importfehler behoben wird, der durch einen Typhinweis in Python verursacht wird.

Zirkulärer Importfehler in Python reproduzieren

Sind Sie beim Versuch, ein Modul zu importieren, schon einmal auf einen Fehler gestoßen? In diesem Fall erhalten wir einen Fehler, der unten gezeigt wird.

from My_Module_A import My_FUNC_A


def My_main_Func():
    My_FUNC_A()


if __name__ == "main":
    My_main_Func()

Ausgang:

ImportError: cannot import name 'My_FUNC_A' from partially initialized module 'My_Module_A' (most likely due to a circular import)

Es sagt uns, dass die wahrscheinlichste Ursache ein zirkulärer Import ist. Hier ist der einfachste und gebräuchlichste Weg, wie diese Art von Importzyklus zur Laufzeit passieren kann. main hat versucht, etwas aus My_Module_A zu importieren.

zirkulärer Python-Import - Beispiel eins

Es beginnt also mit der Initialisierung von My_Module_A und der Ausführung des Codes von My_Module_A, aber die erste Zeile in My_Module_A besteht darin, etwas aus dem Modul MY_Module_B zu importieren.

Es stoppt also die Initialisierung des Moduls My_Module_A, da MY_Module_B zuerst initialisiert werden muss. Und dann springt es zu MY_Module_B und beginnt stattdessen mit der Ausführung dieses Codes.

Aber die erste Zeile im Modul MY_Module_B wird etwas von My_Module_A benötigt, also stoppt es die Ausführung von MY_Module_B und geht zu My_Module_A über.

Und es möchte mit der Initialisierung von My_Module_A beginnen, stellt aber fest, dass sich My_Module_A bereits im Initialisierungsprozess befindet. Da tritt also der Fehler auf.

Wir können sehen, wie sich diese Kette von Ereignissen im Traceback entfaltet. Hier sehen wir in main.py; Es versucht, die Funktion My_FUNC_A aus dem Modul My_Module_A zu importieren, was den Import der Funktion MY_Func_B2 aus dem Modul MY_Module_B ausgelöst hat.

Und MY_Module_B.py löste den Import der Funktion My_FUNC_A erneut aus dem Modul My_Module_A aus, wo der letzte Fehler auftrat.

Traceback (most recent call last):
  File "c:\Users\Dell\Desktop\demo\main.py", line 1, in <module>
    from My_Module_A import My_FUNC_A
  File "c:\Users\Dell\Desktop\demo\My_Module_A.py", line 1, in <module>
    from MY_Module_B, import MY_Func_B2
  File "c:\Users\Dell\Desktop\demo\MY_Module_B.py", line 1, in <module>
    from My_Module_A import My_FUNC_A
ImportError: cannot import name 'My_FUNC_A' from partially initialized module 'My_Module_A' (most likely due to a circular import) (c:\Users\Dell\Desktop\demo\My_Module_A.py)

Wir haben einen Importzeitzyklus zwischen den Modulen My_Module_A und MY_Module_B, aber wenn Sie sich die Namen ansehen, die wir importiert haben, verwenden wir sie nicht zur Importzeit.

Wir verwenden sie, wenn diese Funktion My_FUNC_A() oder wenn diese Funktion MY_Func_B2() aufgerufen wird. Da wir diese Funktionen nicht zur Importzeit aufrufen, gibt es keine echte zyklische Abhängigkeit, was bedeutet, dass wir diesen Importzyklus eliminieren können.

Beheben Sie den zirkulären Importfehler in Python

Es gibt drei gängige Möglichkeiten, dieses Problem zu lösen; Die erste besteht darin, Ihren Import zu nehmen und ihn in die Funktion einzufügen.

In Python können Importe jederzeit stattfinden, auch wenn eine Funktion aufgerufen wird; Wenn Sie diese Funktion MY_Func_B1() benötigen, könnte dies der Import innerhalb der Funktion My_FUNC_A() sein.

def My_FUNC_A():
    from MY_Module_B import MY_Func_B1

    MY_Func_B1()

Es könnte sogar noch effizienter sein, denn wenn Sie die Funktion My_FUNC_A() nie aufrufen, dann müssen Sie möglicherweise MY_Func_B1 überhaupt nicht importieren. Nehmen wir zum Beispiel an, Sie haben eine Reihe verschiedener Funktionen in My_Module_A, und viele verschiedene verwenden diese Funktion MY_Func_B1().

In diesem Fall wäre die bessere Lösung, das Modul MY_Module_B direkt zu importieren und in den Funktionen, in denen Sie es verwenden, den längeren Namen wie MY_Module_B.MY_Func_B1() zu verwenden. Machen Sie also weiter und führen Sie diese Konvertierung für alle am Zyklus beteiligten Module durch.

import MY_Module_B


def My_FUNC_A():
    MY_Module_B.MY_Func_B1()

Mal sehen, wie dies den Importzyklus bei Laufzeitfehler behebt. Zuerst versucht unsere Funktion My_main_Func(), etwas aus My_Module_A zu importieren, wodurch My_Module_A gestartet wird. Dann importieren wir in My_Module_A.py das Modul MY_Module_B, das den Start von MY_Module_B auslöst.

Wenn wir in MY_Module_B.py zum Importmodul My_Module_A gelangen, hat das Modul bereits mit der Initialisierung begonnen; dieses Modulobjekt existiert technisch. Da es existiert, beginnt es nicht, dies erneut auszuführen.

Es sagt nur, okay, das existiert, also fährt das Modul MY_Module_B fort, beendet seinen Import, und nachdem dieser Import abgeschlossen ist, beendet das Modul My_Module_A den Import.

zirkulärer Python-Import - Beispiel zwei

Ausgang:

15

Die dritte und letzte übliche Möglichkeit, diesen Importzyklus zu eliminieren, besteht darin, die beiden Module zusammenzuführen. Also wieder keine Importe, keine Importzyklen; Wenn Sie jedoch überhaupt zwei oder vielleicht sogar mehr Module hatten, hatten Sie sie wahrscheinlich aus einem bestimmten Grund.

Wir werden Ihnen nicht nur empfehlen, alle Ihre Module zu nehmen und sie zu einem zusammenzuführen; das wäre albern. Daher sollten Sie wahrscheinlich eine der ersten beiden Methoden bevorzugen.

Quellcode der Datei My_Module_A.py.

import MY_Module_B


def My_FUNC_A():
    print(MY_Module_B.MY_Func_B1())

Quellcode der Datei MY_Module_B.py.

import My_Module_A


def MY_Func_B1():
    return 5 + 10


def MY_Func_B2():
    My_Module_A.My_FUNC_A()

Quellcode der Datei main.py.

from My_Module_A import My_FUNC_A


def My_main_Func():
    My_FUNC_A()


if __name__ == "main":
    My_main_Func()

Beheben Sie den zirkulären Importfehler, der durch den Typhinweis in Python verursacht wurde

Schauen wir uns ein weiteres Beispiel an, die folgende häufigste Art von Importzyklus, die Sie aufgrund der Verwendung von Typhinweisen ausführen werden. Dieses Beispiel unterscheidet sich vom vorherigen, da Typanmerkungen für eine Funktions- oder Klassendefinition definiert werden.

Wenn die Funktion definiert ist oder die Klasse definiert, aber nicht definiert ist, wenn wir sie ausführen, ist der häufigste Anwendungsfall von Typhinweisen nicht einmal zur Laufzeit. Wenn uns nur die statische Analyse wichtig ist, brauchen wir nicht einmal den Import zur Laufzeit durchzuführen.

Das Modul typing stellt diese Variable zur Verfügung, TYPE_CHECKING ist zur Laufzeit eine falsche Konstante. Wenn wir also alle Importe, die wir für die Typprüfung benötigen, in eine davon stecken, wird zur Laufzeit nichts passieren.

Um die Importschleife ganz zu vermeiden, erhalten Sie beim Importieren dieser Klasse Mod_A_class einen Namensfehler, da dieser Name Mod_A_class nicht importiert wurde.

zirkulärer Python-Import – Beispiel drei

Ausgang:

Traceback (most recent call last):
  File "c:\Users\Dell\Desktop\demo\main.py", line 1, in <module>
    from My_Module_A import My_FUNC_A
  File "c:\Users\Dell\Desktop\demo\My_Module_A.py", line 9, in <module>
    from MY_Module_B, import MY_Func_B1
  File "c:\Users\Dell\Desktop\demo\MY_Module_B.py", line 22, in <module>
    class Mod_b_class:
  File "c:\Users\Dell\Desktop\demo\MY_Module_B.py", line 23, in Mod_b_class
    def __init__(self,a : Mod_A_class):
NameError: name 'Mod_A_class' is not defined

Um dies zu beheben, fügen Sie dies aus __future__ import annotations hinzu. Was macht das?

Es ändert die Funktionsweise von Anmerkungen; Anstatt Mod_b_class als Namen auszuwerten, werden alle Annotationen in Strings umgewandelt.

zirkulärer Python-Import – Beispiel vier

Hier bekommt man nun also zur Laufzeit keinen Fehler mehr, denn obwohl der Name Mod_b_class nicht importiert wird, existiert der String Mod_b_class durchaus. Sie sollten sich jedoch darüber im Klaren sein, dass, wenn Sie einen dieser Importe aus __future__ durchführen, dies nicht unbedingt für immer ein garantiertes Verhalten ist.

Dies führt nun dazu, dass Ihre Anmerkungen als Zeichenfolgen behandelt werden, aber es gibt einen konkurrierenden Vorschlag, sie nur träge auszuwerten. Daher werden sie nicht ausgewertet, es sei denn, Sie versuchen, sie anzusehen und Ihre Typhinweise zur Laufzeit zu überprüfen.

Dies wird wahrscheinlich keinen Unterschied für Ihre Codebasis machen, aber Sie sollten sich dessen bewusst sein. Eine alternative Lösung ist, statt from module imports wieder module importieren zu verwenden, und Sie benötigen weiterhin die annotations von __future__.

zirkulärer Python-Import – Beispiel fünf

Vollständiger Quellcode des Moduls My_Module_A.py.

# from __future__ import annotations

# from typing import TYPE_CHECKING

# from MY_Module_B import MY_Func_B1

# if TYPE_CHECKING:
#     from MY_Module_B import Mod_b_class

# def My_FUNC_A():
#     b : Mod_b_class = MY_Func_B1()


# class Mod_A_class:
#     def __init__(self,b : Mod_b_class):
#         self.b=b


from __future__ import annotations
import MY_Module_B


def My_FUNC_A():
    b: MY_Module_B.Mod_b_class = MY_Module_B.MY_Func_B1()


class Mod_A_class:
    def __init__(self, b: MY_Module_B.Mod_b_class):
        self.b = b

Vollständiger Quellcode des Moduls MY_Module_B.py.

from __future__ import annotations

import My_Module_A


def MY_Func_B1():
    ...


class Mod_b_class:
    def __init__(self, a: My_Module_A.Mod_A_class):
        self.a = a

Vollständiger Quellcode der Datei main.py.

from My_Module_A import My_FUNC_A


def My_main_Func():
    My_FUNC_A()


if __name__ == "main":
    My_main_Func()
Salman Mehmood avatar Salman Mehmood avatar

Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.

LinkedIn

Verwandter Artikel - Python Import