it-swarm.com.de

Zirkulare Importabhängigkeit in Python

Nehmen wir an, ich habe die folgende Verzeichnisstruktur:

a\
    __init__.py
    b\
        __init__.py
        c\
            __init__.py
            c_file.py
        d\
            __init__.py
            d_file.py

Im a-Paket __init__.py wird das c-Paket importiert. Aber c_file.py importiert a.b.d.

Das Programm schlägt fehl, da b nicht vorhanden ist, wenn c_file.py versucht, a.b.d zu importieren. (Und es existiert wirklich nicht, weil wir gerade dabei waren, es zu importieren.)

Wie kann dieses Problem behoben werden?

65
Ram Rachum

Wenn a von c und c von a abhängt, sind sie dann nicht tatsächlich die gleiche Einheit?

Sie sollten wirklich untersuchen, warum Sie a und c in zwei Pakete aufgeteilt haben. Entweder Sie haben Code, den Sie in ein anderes Paket aufteilen sollten (damit beide von diesem neuen Paket abhängen, aber nicht von einander), oder Sie sollten sie zusammenführen in einem Paket.

Sie können den Import verschieben, zum Beispiel in a/__init__.py:

def my_function():
    from a.b.c import Blah
    return Blah()

das heißt, den Import zurückstellen, bis er wirklich benötigt wird. Ich würde mir jedoch auch meine Paketdefinitionen/-verwendungen genau ansehen, da eine zyklische Abhängigkeit, wie die, auf die hingewiesen wird, auf ein Designproblem hindeuten könnte.

147
Dirk

Ich habe mich das ein paar Mal gefragt (normalerweise, wenn es um Modelle geht, die sich gegenseitig kennen müssen). Die einfache Lösung besteht darin, das gesamte Modul zu importieren und auf das zu verweisen, was Sie benötigen.

Also statt zu tun 

from models import Student

in einem und 

from models import Classroom

in der anderen einfach tun

import models

in einem von ihnen rufen Sie models.Classroom an, wenn Sie es brauchen.

24
zachaysan

Das Problem ist, dass bei der Ausführung in einem Verzeichnis standardmäßig nur die Pakete, die Unterverzeichnisse sind, als Kandidatenimport sichtbar sind. Sie können also keine Datei.b.d importieren. Sie können jedoch b.d importieren. da b ein Unterpaket von a ist.

Wenn Sie wirklich a.b.d in c/__init__.py importieren möchten, können Sie dies tun, indem Sie den Systempfad in ein Verzeichnis über a und den Import in a/__init__.py in Import a.b.c. ändern.

Ihr a/__init__.py sollte so aussehen:

import sys
import os
# set sytem path to be directory above so that a can be a 
# package namespace
DIRECTORY_SCRIPT = os.path.dirname(os.path.realpath(__file__)) 
sys.path.insert(0,DIRECTORY_SCRIPT+"/..")
import a.b.c

Eine zusätzliche Schwierigkeit tritt auf, wenn Sie Module in c als Skripts ausführen möchten. Hier existieren die Pakete a und b nicht. Sie können __int__.py im Verzeichnis c hacken, um den Pfad sys.path auf das Verzeichnis der obersten Ebene zu verweisen, und anschließend __init__ in beliebigen Modulen in c importieren, um den vollständigen Pfad zum Importieren von a.b.d verwenden zu können. Ich bezweifle, dass es eine gute Praxis ist, __init__.py zu importieren, aber es hat sich für meine Anwendungsfälle bewährt.

0
John Gilmer

Ich schlage folgendes Muster vor. Wenn Sie es verwenden, werden die automatische Vervollständigung und die Tippfunktion ordnungsgemäß ausgeführt.

cyclic_import_a.py

import playground.cyclic_import_b

class A(object):
    def __init__(self):
        pass

    def print_a(self):
        print('a')

if __== '__main__':
    a = A()
    a.print_a()

    b = playground.cyclic_import_b.B(a)
    b.print_b()

cyclic_import_b.py

import playground.cyclic_import_a

class B(object):
    def __init__(self, a):
        self.a: playground.cyclic_import_a.A = a

    def print_b(self):
        print('b1-----------------')
        self.a.print_a()
        print('b2-----------------')

Mit dieser Syntax können Sie die Klassen A und B nicht importieren

from playgroud.cyclic_import_a import A
from playground.cyclic_import_b import B

Sie können den Parametertyp a nicht in der Klasse B __ init __ -Methode deklarieren, aber Sie können ihn auf diese Weise "umwandeln":

def __init__(self, a):
    self.a: playground.cyclic_import_a.A = a
0
RaamEE