平たく言えば、Python のいわゆる名前空間はコンテナとして理解できます。このコンテナには多くの識別子を保持できます。異なるコンテナ内にある同じ名前の識別子は、互いに競合しません。 Python の名前空間を理解するには、次の 3 つのルールをマスターする必要があります:
まず、 割り当て (明示的な割り当てと暗黙的な割り当てを含む) によって識別子が生成され、割り当ての場所によって識別子が配置される名前空間 が決まります。
2 番目に、 関数定義 (def と lambda を含む) は新しい名前空間 を生成します。
3 番目、Python が識別子を検索する順序は「LEGB」です。
いわゆる「LEGB」は、Python の 4 層の名前空間の英語名の略称です。
最内層はL(ローカル)であり、関数定義内にあることを意味しており、この関数には関数定義が含まれていない。
2 番目の層 E (囲み関数) は関数定義で表現されますが、この関数には関数の定義も含まれています。実際には、L 層と E 層は相対的なものにすぎません。
3 番目の層 G (グローバル) は、モジュールの名前空間、つまり、関数ではなく .py ファイルで定義された識別子を参照します。
4 番目のレイヤー B (builtin) は、Python インタープリターの開始時にすでに使用可能な名前空間を指します。これは、Python インタープリターの開始時に __builtin__ モジュールが自動的にロードされるため、このモジュールのリストと str が組み込まれています。 in 関数はレイヤー B の名前空間にあります。
これら 3 つのルールは、例を通してさらに明確になります。次の例に示すように:
>>> g = int('0x3', 0) >>> def outFunc(): e = 2 g = 10 def inFunc(): l = 1 return g + e return inFunc() >>> outFunc() ===> 12
このコードの識別子を詳しく見てみましょう。
1 行目では、最初のルール「代入により識別子が生成される」が適用され、識別子 g が生成されます。 「割り当ての場所によって、識別子が配置される名前空間が決まります。 g は関数定義にないため、g は 'G' 層名前空間にあります。」この行には int という別の識別子があります。では、int はどこで定義されているのでしょうか? int は組み込み関数であり、__builtin__ モジュールで定義されているため、int は 'B' のレイヤー名前空間にあります。
行 2 は最初のルールを適用します。 def には暗黙的な割り当てプロセスが含まれているため、この行では outFunc が関数定義内にありません。したがって、outFunc は 'G' 層の名前空間にあります。さらに、この行は 2 番目のルールも適用し、新しい名前空間を作成します。
行 3 では、最初のルールを適用して識別子 e を生成します。これは関数定義内にあり、内部に関数定義があるため、e は「E」レイヤー名前空間内にあります。
4 行目では、最初のルールが適用され、識別子 g が生成されます。これは、e のような「E」層名前空間の外側にあることに注意してください。この g は、名前空間が異なるため、最初の行の g とは異なります。
5 行目では、最初のルールを適用し、「E」層名前空間に識別子 inFunc を生成します。行 2 と同様、関数を定義するこの行でも新しい名前空間が作成されます。
行 6 は最初のルールを適用し、識別子 l を生成します。この l は関数内にあり、この関数内には他の関数が定義されていないため、l は「L」レイヤー名前空間にあります。
7 行目、3 番目のルールが適用されます。Python インタプリタは最初に識別子 g を確認し、LEGB の順序で検索し、最初に L 層 (つまり、inFunc 内) を探しますが、見つかりません。レイヤー E を再度検索します。レイヤー E が 1 つあり、値は 10 です。したがって、ここでの g の値は 10 です。検索プロセスが終了すると、「G」レイヤーはそれ以上見つかりません。 e を求めるプロセスは同じで、e の値は 2 です。したがって、9 行目の結果は 12 になります。
実際、いわゆる「LEGB」は学術的な表現を容易にするために作成されました。 Python が識別子の値を見つける方法を知っている限り、プログラマがどの識別子がどのレベルにあるかを伝えることは意味がありません。実際、価値を見つけるプロセスは直感的に理解しやすいです。
上記の例からわかるように、同じ識別子が異なる名前空間で定義されていても問題はなく、競合は発生しません。識別子の値を見つけるプロセスは常に現在のレイヤーから開始され、最初に検出されるのは識別子の値です。 「B」層識別子はすべてのモジュール (.py ファイル) で使用可能であり、「G」層識別子は現在のモジュール (.py ファイル) 内で使用可能であるとも言えます。識別子は現在の関数内で使用できます。
グローバルステートメントの使用法を説明するために別の例を見てみましょう。コードは次のようになります:
>>> g = 'global' >>> s = 'in' >>> def out(): g = 'out' def inter(): global g print s,g inter() >>> out() ===> 'in global'
レイヤー内に g が 2 つありますが、global ステートメントを使用すると、「G」レイヤーの識別子を参照していることがわかります。つまり、7 行目の g は 1 行目で生成された g を参照し、その値は「global」です。
最後に、プログラミング時に注意を払い、同じ識別子を使用しない限り、基本的に名前空間に関連する問題は回避できます。また、関数内の上位レベルの名前空間では識別子を使用しないようにしてください。識別子を使用する必要がある場合は、関数の独立性を維持するのに役立つパラメーターの受け渡しを使用することをお勧めします。