web-dev-qa-db-ja.com

Pythonの循環インポート依存関係

私は次のディレクトリ構造を持っているとしましょう:

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

aパッケージの__init__.pycパッケージがインポートされます。しかし、c_file.pyインポートa.b.d

プログラムは、bが存在しないと言って、c_file.pyインポートしようとするa.b.d。 (そして、私たちはそれをインポートしている最中だったので、それは本当に存在しません。)

この問題はどのように修正できますか?

69
Ram Rachum

Aがcに依存し、cがaに依存する場合、実際には同じユニットではないでしょうか?

Aとcを2つのパッケージに分割した理由を実際に調べる必要があります。いくつかのコードがあるため、別のパッケージに分割する必要があります(両方とも新しいパッケージに依存するが、互いに依存しないようにするため)。 1つのパッケージに。

たとえば、a/__init__.py

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

つまり、本当に必要になるまでインポートを延期します。ただし、指摘されているような周期的な依存関係は設計上の問題を示している可能性があるため、パッケージの定義/使用についても詳しく調べます。

150
Dirk

私はこれを数回疑問に思いました(通常、お互いについて知る必要があるモデルを扱っているとき)。簡単な解決策は、モジュール全体をインポートしてから、必要なものを参照することです。

ので、代わりに

from models import Student

で、そして

from models import Classroom

他では、ただやる

import models

それらの1つで、models.Classroomを必要なときに呼び出します。

25
zachaysan

問題は、ディレクトリから実行する場合、デフォルトではサブディレクトリであるパッケージのみがインポート候補として表示されるため、a.b.dをインポートできないことです。ただし、b.dをインポートできます。 bはaのサブパッケージであるため。

本当にc/__init__.pyのa.b.dをインポートしたい場合、システムパスをa上の1つのディレクトリに変更し、a/__init__.pyのインポートをa.b.cに変更することでこれを実現できます。

a/__init__.pyは次のようになります。

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

Cのモジュールをスクリプトとして実行する場合、さらに困難が生じます。ここでは、パッケージaとbは存在しません。 cディレクトリの__int__.pyをハックしてsys.pathを最上位ディレクトリにポイントし、c内のモジュールで__init__をインポートして、フルパスを使用してa.b.dをインポートできます。 __init__.pyをインポートするのは良い習慣だとは思いませんが、私のユースケースではうまくいきました。

0
John Gilmer

次のパターンをお勧めします。これを使用すると、オートコンプリートとタイプヒンティングが適切に機能します。

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-----------------')

この構文を使用してクラスAおよびBをインポートすることはできません

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

クラスB __ init __メソッドでパラメーターaの型を宣言することはできませんが、次のように「キャスト」できます。

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