Python 関数のスコープ規則


Python は静的スコープの言語ですが、それ自体は動的言語です。言い換えれば、Python の変数のスコープはソース コード内の位置によって決まります。これは C と似ていますが、Python と C のスコープの違いは依然として非常に明白です。


次に、Python のスコープ ルールについて説明し、スコープに関する Python と C の違いについても説明します。

Python 2.0 以前のバージョンでは、Python はローカル スコープ、グローバル スコープ、組み込みスコープの 3 種類のスコープのみをサポートしていました。Python 2.2 では、Python は新しいロールであるドメイン --- ネストされたスコープを正式に導入しました。 ; Python 2.1 では、ネストされたスコープをオプションとしてオンにすることができます; ネストされたスコープの導入により、本質的に Python でのクロージャのサポートが実装されます クロージャに関する知識、ネット上に多くの説明があるため、ここでは詳しく説明しません。これに応じて、変数の検索順序も以前の LGB から LEGB に変更されます (L: ローカル、E: 囲み、G: グローバル、B: 組み込み)。


Python では、コード ブロックに新しいスコープを導入することはできません。これは C:


とは大きく異なります。
#include<stdio.h>int main() {    if(2 > 0) {        int i = 0;
    }
    printf("i = %d", i);    return 0;
}

このコードでは、if 句でローカル スコープが導入されており、変数 i はこのローカル スコープ内に存在しますが、外部からは見えないため、その後、printf 関数で変数 i を参照すると、コンパイル エラーが発生します。

ただし、これは Python の場合には当てはまりません:

if True:
    i = 0print i

このコードでは、if 句はローカル スコープを導入しておらず、変数 i はまだしたがって、グローバル スコープでは、変数 i は後続の print ステートメントから参照できます。

実際、Python では、モジュール、クラス、関数のみが新しいスコープを導入し、他のコード ブロックは新しいスコープを導入しません。

Python では、変数を使用する前に事前に宣言する必要はありませんが、実際に使用する前に変数をオブジェクトにバインドする必要があります。名前バインディングにより、現在のスコープに新しい変数が導入されます。 . 、現在のスコープのどこでこの名前バインディングが発生しても、外側のスコープで同じ名前の変数をシールドします。

def f():    print i
f()


実行結果は次のように表示されます: NameError: グローバル名 'i' が定義されていません。 Python は最初に関数 f のローカル スコープで変数 i を検索しますが、検索は失敗します。次に、グローバル スコープと組み込みスコープで変数 i を検索しますが、それでも失敗し、最終的に NameError 例外をスローします。

i = 0def f():
    i = 8    print i
f()print i


実行結果は、8 と 0 を示します。 i = 8 は名前バインディング操作です。関数 f のローカル スコープに新しい変数 i が導入され、グローバル変数 i がシールドされます。したがって、 f 内の print ステートメントはローカル変数 i を参照し、f の外部の print ステートメントはローカル変数 i を参照します。ローカル変数 i。ステートメントが参照するのはグローバル変数 i です。

i = 0def f():    print i
    i = 0
f()


実行結果は次のようになります: UnboundLocalError: 代入前にローカル変数 'i' が参照されました。この例では、関数 f の変数 i はローカル変数ですが、print ステートメントで使用するとき、どのオブジェクトにもバインドされていないため、例外がスローされます。

うわー


対話形式で実行した場合でも、スクリプト ファイルとして実行した場合でも、結果は次のようになります。 NameError: name 'i' が定義されていません。ここでの出力は、最上位スコープ (モジュール スコープ) 内にあるため、前の例とは異なります。モジュールコードの場合、コードは実行前に前処理を受けませんが、関数本体の場合、コードは実行前にすでに前処理を受けているため、スコープ内のどこで名前バインディングが発生しても、それを感じることができます。 Python は静的にスコープされた言語ですが、名前の検索は動的に行われるため、名前の問題は実行時まで発見されません。


Python では、名前バインディングによって、その変数が属するスコープ内に新しい変数が導入され、それがオブジェクトにバインドされます。名前バインディングは、次の状況で発生します。


  1. パラメータ宣言: パラメータ宣言が関数のローカル スコープ内にある 新しいものを導入する変数への;
  2. 代入操作: 変数の最初の代入では、現在のスコープに新しい変数が導入され、その後の代入操作では変数がbe rebind;
  3. クラスと関数の定義: クラスと関数の定義は、クラス名と関数名を変数として現在のスコープに導入します。クラス本体は関数本体は別のスコープを形成します;
  4. #import ステートメント: import ステートメントは、現在のスコープ (通常はグローバル スコープ) に新しい変数を導入します。
  5. #for ステートメント: for ステートメントは、現在のスコープに新しい変数 (ループ変数) を導入します。
  6. Except ステートメント: Except ステートメントは、現在のスコープに新しい変数 (例外オブジェクト) を導入します。


#Python では、クラス定義によって導入されたスコープはメンバー関数からは見えません。これは C や Java とは大きく異なります。ただし、Python では、メンバー関数がクラス本体で定義された変数を参照したい場合は、self またはクラス名を通じてその変数を参照する必要があります。


ネストされたスコープを追加すると、一部のコードがコンパイルに失敗したり、異なる実行結果が得られたりすることがあります。ここで、Python インタープリターは、問題の原因となる可能性のある場所を特定するのに役立ちます。警告を発します。


#locals 関数はすべてのローカル変数を返しますが、ネストされたスコープ内の変数は返しません。実際、ネストされたスコープ内の変数を返す関数はありません。