5

パッケージ構造を次のように考えてみましょう。

myApp
|-- myPackage
|    |-- __init__.py 
|    +-- myModule.py
|-- __init__.py
+-- main.py

myModule.py には、次のような単一のクラスが含まれています。

class MyClass( object ):
    _myList = []

    @classmethod
    def test( cls ):
        cls._myList.append( len( cls._myList ) )
        return cls._myList

    def __init__( self ):
        return

    pass

ご覧のとおり、特別なことは何もありません。リストを静的クラス変数として定義しているだけです。次に、main.py のコードを考えてみましょう。

from myApp.myPackage.myModule import MyClass as MyClassAbs
from myPackage.myModule import MyClass as MyClassRel

if __name__ == '__main__':

    print '\n  myList'
    print 'MyClassAbs().test():', MyClassAbs().test() #< Prints [0].
    print 'MyClassRel().test():', MyClassRel().test() #< Prints [0] but should be [0, 1].
    print 'MyClassAbs.test():', MyClassAbs.test() #< Prints [0, 1] but should be [0, 1, 2].
    print 'MyClassRel.test():', MyClassRel.test() #< Prints [0, 1] but should be [0, 1, 2, 3].

    print '\n  myList ids'
    print id( MyClassAbs().test() )
    print id( MyClassRel().test() )
    print id( MyClassAbs.test() )
    print id( MyClassRel.test() )

    print ''
    print 'MyClassAbs == MyClassRel:', MyClassAbs == MyClassRel #< Prints False
    print 'isinstance( MyClassAbs(), MyClassRel ):', isinstance( MyClassAbs(), MyClassRel ) #< Prints False
    print 'isinstance( MyClassRel(), MyClassAbs ):', isinstance( MyClassRel(), MyClassAbs ) #< Prints False

    pass

問題は、私のコードでは、同じモジュールから同じクラスを 2 回インポートしていますが、異なる方法でインポートしていることです。1 回は絶対インポートとして、もう 1 回は相対インポートとしてインポートしています。コードの最後の部分に見られるように、クラスが同じであっても、それらのモジュールは sys.modules に個別に登録されているため、クラスは等しくありません。

'myApp.myPackage.myModule': <module 'myApp.myPackage.myModule' from '/full/path/myApp/myPackage/myModule.py'>
'myPackage.myModule': <module 'myPackage.myModule' from '/full/path/myApp/myPackage/myModule.py'>

その結果、それらの表現は異なります。

'MyClassAbs': <class 'myApp.myPackage.myModule.MyClass'>
'MyClassRel': <class 'myPackage.myModule.MyClass'>

それで...この変数を静的に設定する方法はありますか?

編集:
上記のコードは、明らかに私の実際の問題を縮小したものにすぎません。実際には、基本的に、フォルダー内に再帰的にネストされたすべてのモジュールを検査し、そこに含まれるクラスを登録するコードがあります。この方法で登録されたすべてのクラスは、次のような共通の構文を使用してインスタンス化できます。

myApp.create( 'myClass' )

これが、同じクラスを指しているが異なる方法でインポートされた 2 つのオブジェクトを持つことがある理由です。1 つは imp.load_module() 呼び出しによって自動的にインポートされ、もう 1 つは従来の import ステートメントによってユーザーによって直接インポートされます。ユーザーがクラスを手動でインポートすることにした場合でも、自動的に登録された同じクラス内で定義された変数と同じ静的クラス変数にアクセスできる必要があります。それが理にかなっていると思いますか?

4

2 に答える 2

8

いいえ。(少なくとも、非常に醜くて壊れやすいハックがないわけではありません。)これらの2つの異なる方法でインポートすると、モジュールは実際には2回インポートされます。Pythonは通常、インポート済みのモジュールを再度インポートすると再利用しますが、これはインポートされた実際のファイルではなく、に相対的なパスに基づいていますsys.pathmyModule.pyしたがって、絶対に1回、比較的1回インポートすると、ファイル全体が実際に2回実行されます。次のようなことをすると、これを確認できます。

from myApp.myPackage import myModule
import myApp.myPackage.myModule as sameModule
import myPackage.myModule
print myModule is sameModule
print myModule is myPackage.myModule

True最初の印刷(最初の2つのインポートは同じパスを経由するFalseため)と2番目の印刷(3番目のインポートは異なるパスを使用するため)を確認する必要があります。これが、に2つのエントリがあるモジュールが表示されている理由でもありますsys.modules

モジュール全体が2回インポートされるため、実際にはMyClassと呼ばれる2つの異なるクラスがあり、1つではありません。これらの2つのクラスに状態を共有させる正当な方法はありません。これは、2つの異なるモジュールから2つの異なるクラスをインポートした場合と同じです。それらはたまたま同じソースファイルで定義されていますが、そのようにダブルインポートすると、リンクされません。(それらをリンクする邪悪な方法があります。つまり、モジュールにコードを含めて、別の名前でインポートされているかどうかを手動で確認してから、手動で操作しsys.modulesて、現在インポートされているモジュールをすでにインポートされているモジュールに設定することができます。 。しかし、これは悪い考えです。インポートされているものが本当に同じモジュールであるかどうかを判断するのが難しい場合があり、また踏みつけているためです。sys.modules元のインポートまたは新しいインポートのいずれかが何らかの理由で失敗した場合、奇妙なバグにつながる可能性があります。)

本当の問題は、なぜモジュールを2回インポートするのかということです。2番目の質問は、なぜ相対インポートを使用しているのかということです。解決策は、絶対インポートを使用してモジュールを1回インポートするだけで、問題は発生しません。

于 2012-11-15T05:41:47.837 に答える
-1

Pythonに関して言えば、他の言語の「静的」変数には多くの答えがあります。私も、この言語に関しては、その考え方が頭を悩ませていると思います。

あなたの最終的な目標が何であるかを伝えるのは難しいですが、モジュールで宣言することにより、'static' 変数 (|cough| 'module' 変数?) を作成できることに注意してください。したがって、myModule は次のような構造になっている可能性があります。

modList = []

class MyClass(object):
    def __init__(self):
        self._myList = [len(modList),]

    def appendLenToModList(self):
        modList.append(len(self._myList))

modList は で初期化さimportれ、シングルトン/静的変数として機能するはずです。本当に直接いじりたい場合は、名前空間を使用して myApp から使用することもできると確信していますmyPackage.myModule.modList。しかし、その使用法と目的を形式化するための一部のアクセサーが推奨される場合があります。

于 2012-11-15T06:28:57.457 に答える