9

私が取り組んでいる大きなアプリケーションでは、複数の人が同じモジュールを異なる方法でインポートします。たとえば、インポートxまたはyからインポートxの副作用は、xが2回インポートされ、誰かがグローバル属性に依存している場合、非常に微妙なバグを引き起こす可能性があります

たとえば、mymodule.py、 main.py 、 init.pyの3つのファイルを持つパッケージmypakcageがあるとします。

mymodule.pyの内容

l = []
class A(object): pass

main.pyの内容

def add(x):
    from mypackage import mymodule
    mymodule.l.append(x)
    print "updated list",mymodule.l

def get():
    import mymodule
    return mymodule.l

add(1)
print "lets check",get()

add(1)
print "lets check again",get()

印刷します

updated list [1]
lets check []
updated list [1, 1]
lets check again []

2つの異なるモジュールに2つのリストがあるため、同様にクラスAは異なります。クラス自体が異なる方法で処理されるため、私には十分に深刻に見えます。たとえば、以下のコードはFalseを出力します。

def create():
    from mypackage import mymodule
    return mymodule.A()

def check(a):
    import mymodule
    return isinstance(a, mymodule.A)

print check(create())

質問:

これを回避する方法はありますか?ただし、モジュールを一方向にインポートする必要があります。これをPythonインポートメカニズムで処理することはできません。djangoコードやその他の場所でも、これに関連するいくつかのバグが見られます。

4

2 に答える 2

5

各モジュールの名前空間は一度だけインポートされます。問題は、それらを異なる方法でインポートしていることです。最初にグローバル パッケージからインポートし、2 番目にローカルでパッケージ化されていないimport. Python はモジュールを異なるものとして認識します。最初のインポートは as として内部的にキャッシュされmypackage.mymodule、2 番目のインポートは as mymoduleonly としてキャッシュされます。

これを解決する方法は、常に絶対インポートを使用することです。つまり、最上位パッケージ以降の絶対インポート パスを常にモジュールに指定します。

def add(x):
    from mypackage import mymodule
    mymodule.l.append(x)
    print "updated list",mymodule.l

def get():
    from mypackage import mymodule
    return mymodule.l

エントリ ポイント (実行するファイルmain.py) もパッケージの外にある必要があることに注意してください。エントリ ポイント コードをパッケージ内に配置する場合は、通常、代わりに小さなスクリプトを実行します。例:

runme.py、パッケージの外側:

from mypackage.main import main
main()

そして、main.pyあなたが追加します:

def main():
    # your code

Jp Calderone によるこのドキュメントは、Python プロジェクトを構造化する (しない) 方法に関する優れたヒントであることがわかりました。それに従えば問題ありません。フォルダに注意してbinください - それはパッケージの外にあります。ここに全文を転載します。

Python プロジェクトのファイルシステム構造

する

  • ディレクトリにプロジェクトに関連する名前を付けます。たとえば、プロジェクトの名前が「Twisted」の場合、そのソース ファイルの最上位ディレクトリに名前を付けますTwisted。リリースするときは、バージョン番号のサフィックスを含める必要があります: Twisted-2.5.
  • ディレクトリTwisted/binを作成し、実行可能ファイルがある場合はそこに配置します。.py Python ソース ファイルであっても、拡張子を付けないでください。プロジェクトの別の場所で定義されたメイン関数のインポートと呼び出し以外のコードをそれらに入れないでください。
  • プロジェクトが単一の Python ソース ファイルとして表現できる場合は、それをディレクトリに配置し、プロジェクトに関連する名前を付けます。たとえば、 Twisted/twisted.py. 複数のソース ファイルが必要な場合は、代わりにパッケージを作成し ( Twisted/twisted/、空の Twisted/twisted/__init__.py)、その中にソース ファイルを配置します。たとえば、 Twisted/twisted/internet.py.
  • 単体テストをパッケージのサブパッケージに入れます (注 - これは、上記の単一の Python ソース ファイル オプションがトリックであったことを意味します。単体テストには常に少なくとも 1 つの他のファイルが必要です)。たとえば、 Twisted/twisted/test/. もちろん、 でパッケージ化し Twisted/twisted/test/__init__.pyます。のようなファイルにテストを配置します Twisted/twisted/test/test_internet.py
  • addTwisted/READMEと Twisted/setup.pyを追加して、それぞれソフトウェアの説明とインストールを行ってください。

してはいけないこと

  • srcまたはという名前のディレクトリにソースを置きますlib。これにより、インストールせずに実行するのが難しくなります。
  • テストを Python パッケージの外に置きます。これにより、インストールされたバージョンに対してテストを実行することが難しくなります。
  • のみを持つパッケージを作成し、 __init__.pyすべてのコードを に入れます__init__.py。パッケージの代わりにモジュールを作成するだけで、より簡単になります。
  • Python がモジュールまたはパッケージをインポートできるようにするための魔法のハックを思いつくようにしてください。ユーザーはそれを含むディレクトリをインポート パスに追加する必要はありません (PYTHONPATHまたは他のメカニズムを介して)。すべてのケースを正しく処理できるわけではなく、ソフトウェアが自分の環境で動作しない場合、ユーザーは怒ります。
于 2009-09-22T11:47:06.227 に答える
3

main.py が実際に実行しているファイルである場合にのみ、これを複製できます。その場合、sys パス上の main.py の現在のディレクトリを取得します。ただし、mypackage をインポートできるように、システム パスも設定されているようです。

そのような状況では、Python は mymodule と mypackage.mymodule が同じモジュールであることを認識せず、この効果が得られます。この変更は次のことを示しています。

def add(x):
    from mypackage import mymodule
    print "mypackage.mymodule path", mymodule
    mymodule.l.append(x)
    print "updated list",mymodule.l

def get():
    import mymodule
    print "mymodule path", mymodule
    return mymodule.l

add(1)
print "lets check",get()

add(1)
print "lets check again",get()


$ export PYTHONPATH=.
$ python  mypackage/main.py 

mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
mymodule path <module 'mymodule' from '/tmp/mypackage/mymodule.pyc'>

ただし、現在のディレクトリに別のメインファイルを追加します。

realmain.py:
from mypackage import main

結果は異なります。

mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>

したがって、パッケージ内にメインの python ファイルがあると思われます。その場合の解決策は、それを行わないことです。:-)

于 2009-09-22T11:46:25.473 に答える