web-dev-qa-db-ja.com

モジュール変数を別のモジュールから変更する方法は?

barという名前のパッケージがあり、bar.py

a = None

def foobar():
    print a

および__init__.py

from bar import a, foobar

次に、このスクリプトを実行します。

import bar

print bar.a
bar.a = 1
print bar.a
bar.foobar()

ここに私が期待するものがあります:

None
1
1

私が得るものは次のとおりです。

None
1
None

誰でも私の誤解を説明できますか?

83
shino

from bar import aを使用しています。 aは、インポートモジュールのグローバルスコープ(またはインポートステートメントが発生するスコープ)のシンボルになります。

aに新しい値を割り当てると、実際の値ではなく、aが指す値も変更されます。 bar.pyimport barを使用して__init__.pyを直接インポートし、bar.a = 1を設定してそこで実験を行ってください。この方法では、実際にこのコンテキストでaの「実際の」値であるbar.__dict__['a']を変更します。

3つの層で少し複雑ですが、bar.a = 1は、実際に__init__.pyから派生したaというモジュールのbarの値を変更します。 aは実際のファイルbar.pyにあるため、foobarが見るfoobarの値は変更しません。変更する場合は、bar.bar.aを設定できます。

これはimportステートメントのfrom foo import bar形式を使用する危険の1つです。これはbarを2つのシンボルに分割します。1つはfoo内からグローバルに表示され、 importステートメントが実行されます。シンボルが指す場所を変更しても、それが指す値は変わりません。

インタラクティブなインタープリターからモジュールをreloadしようとするとき、この種のものはキラーです。

78
aaronasterling

別の言い方をすると、この誤解は非常に簡単であることがわかります。 Python言語リファレンス: でこっそり定義されていますobjectではなくsymbol。Python言語リファレンスにより、これがより明確になり、スパースが少なくなります。

fromフォームはモジュール名をバインドしません。識別子のリストを調べて、ステップ(1)で見つかったモジュール内でそれらのそれぞれを検索し、ローカルネームスペースの名前をobjectが見つかりました。

HOWEVER:

インポートするとき、インポートされたシンボルの現在の値をインポートし、定義されているようにネームスペースに追加します。参照をインポートしていません。値を効果的にインポートしています。

したがって、iの更新された値を取得するには、そのシンボルへの参照を保持する変数をインポートする必要があります。

つまり、インポートは、Javaのimport、C/C++のexternal宣言、またはPerlのuse句とは異なります。

むしろ、Pythonの次のステートメント:

from some_other_module import a as x

k&R Cの次のコードに似ていますlike

extern int a; /* import from the EXTERN file */

int x = a;

(注意:Pythonの場合、「a」と「x」は本質的に実際の値への参照です。INTをコピーするのではなく、参照アドレスをコピーする)

10