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
誰でも私の誤解を説明できますか?
from bar import a
を使用しています。 a
は、インポートモジュールのグローバルスコープ(またはインポートステートメントが発生するスコープ)のシンボルになります。
a
に新しい値を割り当てると、実際の値ではなく、a
が指す値も変更されます。 bar.py
でimport 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
しようとするとき、この種のものはキラーです。
別の言い方をすると、この誤解は非常に簡単であることがわかります。 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をコピーするのではなく、参照アドレスをコピーする)