11

まず第一に、私は、循環輸入のトピックに対する多くの質問と回答がすでにあることを知っています。

答えは多かれ少なかれ、「モジュール/クラス構造を適切に設計すれば、循環インポートは必要ありません」です。それは本当です。私は現在のプロジェクトのために適切なデザインを作るために一生懸命努力しました、私はこれで成功したと思います。

しかし、私の特定の問題は次のとおりです。チェックするクラスを含むモジュールによってすでにインポートされているモジュールでタイプチェックが必要です。しかし、これはインポートエラーをスローします。

そのようです:

foo.py:

from bar import Bar

class Foo(object):

    def __init__(self):
        self.__bar = Bar(self)

bar.py:

from foo import Foo

class Bar(object):

    def __init__(self, arg_instance_of_foo):
        if not isinstance(arg_instance_of_foo, Foo):
            raise TypeError()

解決策1:文字列比較によってタイプをチェックするように変更すると、機能します。しかし、私はこのソリューションが本当に好きではありません(文字列の比較は、単純な型チェックにはかなり費用がかかり、リファクタリングに関しては問題が発生する可能性があります)。

bar_modified.py:

from foo import Foo

class Bar(object):

    def __init__(self, arg_instance_of_foo):
        if not arg_instance_of_foo.__class__.__name__ == "Foo":
            raise TypeError()

解決策2: 2つのクラスを1つのモジュールにパックすることもできます。しかし、私のプロジェクトには「Bar」の例のように多くの異なるクラスがあり、それらを異なるモジュールファイルに分けたいと思います。

私自身の2つの解決策は私には選択肢がありません:この問題のより良い解決策はありますか?

4

4 に答える 4

7

最善の解決策は、タイプをチェックしないことです。

もう1つの解決策は、のインスタンスを作成せず、まったく参照しないFooBar、両方のクラスがロードされるまでです。最初のモジュールが最初にロードされる場合は、ステートメントが実行されるまで、Barまたは参照を作成しないでください。同様に、2番目のモジュールが最初にロードされる場合は、ステートメントが実行されるまで、または参照を作成しないでください。Barclass FooFooFooclass Bar

これは基本的にのソースでありImportError、代わりに「importfoo」と「importbar」を実行し、foo.Foo現在使用している場所と現在使用している場所でFoo使用すると回避できます。これを行う際に、またはが作成されるまでどちらかを参照しなくなります。これは、両方が作成されるまで発生しないことを願っています(そうでない場合は、を取得します)。bar.BarBarFooBarAttributeError

于 2010-03-17T11:59:05.530 に答える
6

interface特定のタイプではなく、 (ABC- Pythonの抽象基本クラス)に対してプログラミングできますBar。これは、多くの言語でパッケージ/モジュールの相互依存関係を解決するための古典的な方法です。概念的には、オブジェクトモデルの設計も改善されるはずです。

あなたの場合IBar、他のモジュールでインターフェースを定義します(または、Fooクラスを含むモジュールでさえ-その使用法によって異なりますabc)。コードを作成すると、次のようになります。

foo.py:

from bar import Bar, IFoo

class Foo(IFoo):
    def __init__(self):
        self.__bar = Bar(self)

# todo: remove this, just sample code
f = Foo()
b = Bar(f)
print f
print b
x = Bar('do not fail me please') # this fails

bar.py:

from abc import ABCMeta
class IFoo:
    __metaclass__ = ABCMeta

class Bar(object):
    def __init__(self, arg_instance_of_foo):
        if not isinstance(arg_instance_of_foo, IFoo):
            raise TypeError()
于 2010-03-17T12:10:08.037 に答える
2

次のように、bar.pyでのインポートを延期することができます。

class Bar(object):

    def __init__(self, arg_instance_of_foo):
        from foo import Foo
        if not isinstance(arg_instance_of_foo, Foo):
            raise TypeError()
于 2010-03-17T11:59:47.390 に答える
2

重複の可能性:循環インポートなしのPythonタイプヒント

フォワードリファレンス(PEP 484-タイプヒント)を使用する必要があります。

型ヒントにまだ定義されていない名前が含まれている場合、その定義は後で解決されるように文字列リテラルとして表現される場合があります。

したがって、代わりに:

class Tree:
    def __init__(self, left: Tree, right: Tree):
        self.left = left
        self.right = right

行う:

class Tree:
    def __init__(self, left: 'Tree', right: 'Tree'):
        self.left = left
        self.right = right
于 2019-07-13T07:11:19.613 に答える