23

私は約30のユニークなモジュールを含むプロジェクトで働いています。あまりうまく設計されていなかったので、プロジェクトにいくつかの新しい機能を追加するときに循環インポートを作成するのが一般的です。

もちろん、循環インポートを追加すると、気づきません。AttributeError: 'module' object has no attribute 'attribute'明確に定義した場所のようなエラーが発生したときに、循環インポートを行ったことが非常に明白な場合があります'attribute'。ただし、使用方法が原因でコードが例外をスローしない場合もあります。

だから、私の質問に:

循環インポートがいつどこで発生しているかをプログラムで検出することは可能ですか?

これまでに考えられる唯一の解決策importTrackingは、dictを含むモジュール、インクリメントimportingModulesする関数、 1より大きい場合はエラーをスローする関数、およびデクリメントする関数を用意することです。他のすべてのモジュールは次のようになります。importInProgress(file)importingModules[file]importComplete(file)importingModules[file]

import importTracking
importTracking.importInProgress(__file__)
#module code goes here.
importTracking.importComplete(__file__)

しかし、それは本当に厄介に見えます、それを行うためのより良い方法がなければなりませんよね?

4

4 に答える 4

10

すべてのモジュールを変更する必要をなくすために、インポート追跡機能をインポートフックに固定するか、カスタマイズさ__import__れたものに組み込みに固定することができます__import__。インポートされるモジュールはすでににsys.modulesあります。これは、循環インポートの場合です。

実装には、「インポート中」のモジュールのセットを使用します。たとえば、(ベンジャオミング編集:オリジナルから派生した作業スニペットを挿入する):

beingimported = set()
originalimport = __import__
def newimport(modulename, *args, **kwargs):
    if modulename in beingimported:
        print "Importing in circles", modulename, args, kwargs
        print "    Import stack trace -> ", beingimported
        # sys.exit(1) # Normally exiting is a bad idea.
    beingimported.add(modulename)
    result = originalimport(modulename, *args, **kwargs)
    if modulename in beingimported:
        beingimported.remove(modulename)
    return result
import __builtin__
__builtin__.__import__ = newimport
于 2010-03-09T01:22:03.627 に答える
1

例外がスローされない場合に見られるように、すべての循環インポートが問題になるわけではありません。

それらが問題になると、次にテストを実行しようとしたときに例外が発生します。これが発生したときにコードを変更できます。

この状況から必要な変更は見当たりません。

問題がない場合の例:

a.py

import b
a = 42
def f():
  return b.b

b.py

import a
b = 42
def f():
  return a.a
于 2010-03-09T01:19:55.097 に答える
1

Pythonでの循環インポートは、PHPに含まれるものとは異なります。

Pythonでインポートされたモジュールは、インポート「ハンドラー」に初めてロードされ、プロセスの間そこに保持されます。このハンドラーは、後続のインポートごとに、そのモジュールからインポートされたものすべてにローカル名前空間の名前を割り当てます。モジュールは一意であり、そのモジュール名への参照は、インポートされた場所に関係なく、常に同じロードされたモジュールを指します。

したがって、循環モジュールインポートがある場合、各ファイルのロードは1回行われ、その後、各モジュールには、その名前空間に作成された他のモジュールに関連する名前が付けられます。

もちろん、両方のモジュール内で特定の名前を参照する場合(反対のモジュールのインポートで参照されるクラス/関数定義の前に循環インポートが発生する場合)に問題が発生する可能性がありますが、その場合はエラーが発生します。

于 2010-03-09T01:28:47.823 に答える
0

importを使用する__builtin__.__import__()ので、モンキーパッチを適用すると、あらゆる場所のすべてのインポートで変更が取得されます。ただし、循環インポートは必ずしも問題ではないことに注意してください。

于 2010-03-09T01:20:21.333 に答える