ローカル名の静的検出
Python がローカル スコープを検出するとき: 実行時の割り当てではなく、Python がコードをコンパイルするときに検出されます。
通常、関数内でコピーされていない名前は、それが含まれるモジュール内で検索されます:
>>> x=99
>>> def selector():
... print x
...
>>> selector()
99
ただし:
>>> def selector():
... print x
.. x= 100
...
>>> selector()
トレースバック (最後の呼び出し):
ファイル "
ファイル "
UnboundLocalError の 2 行目: 代入
の前に参照されたローカル変数 'x' で、未定義の名前エラーが発生します。
対話的に入力されるか、モジュールからインポートされると、Python はこのコードを読み取ってコンパイルします。コンパイル時に、Python は x の割り当てを調べ、x が関数内の任意の場所のローカル名であると判断します。その後、関数が実際に実行されて print が実行されると、割り当てはまだ行われていないため、Python は未定義の名前を使用していると表示します。彼の命名規則によれば、代入の前にローカル x を使用する必要があります。
解決策:
グローバル x を出力したい場合は、グローバル ステートメントで宣言する必要があります: (これは、代入によってローカル x ではなくグローバル x も変更されることを意味します)
>>> def selector() :
... グローバル x
... print x
... x=88
...
>>> selector()
99
の場合グローバル割り当てを出力し、ローカル割り当てを設定し、それを含むモジュールをインポートして修飾してグローバル バージョンを取得します:
>>> x=99
>>> def selector():
。 .. import __main__
... print __main__.x
... x=88
... print x
...
>>> selector()
99
88
修飾 (.x 部分) 名前空間オブジェクトから値を取得します。対話型環境の名前空間は、__main__ というモジュールです。
ネストされた関数はネストされたスコープを持つことができます (新しいバージョンと古いバージョンでは異なります)
>>> def external(x):
... def inner(i):
。 . print i,
... if i: inner(i-1)
... inner(x)
...
>>> external(3)
3 2 1 0
デフォルト値で参照を保存
>>> def external(x):
... def inner(i,self=inner):
... print i,
... if i:self(i-1)
... inner(x)
...
>>> external(3)
トレースバック (最後に行われた最新の呼び出し):
ファイル "
ファイル "
UnboundLocalError: ローカル変数 'inner' が代入前に参照されました
解決策の原則:シンプルな方法は常に最も正しい方法です
>>> def inner(i):
... print i,
... if i:inner(i-1)
.. 。
>>> def inner(x):
... inner(x)
...
>>> inner(3)
3 2 1 0
デフォルト可変object
>>> def saver(x=[]):
... x.append(1)
... print x
...
>> > saver( [2])
[2, 1]
>>> saver()
[1]
>>> saver()
[1, 1]
>>> saver()
[1, 1, 1]
問題は、ここにはリスト オブジェクトが 1 つしかないことです (def の実行時に生成されるものです)。関数が呼び出されるたびに、新しいリスト オブジェクトが取得されるのではなく、元のリスト オブジェクトが増加します。
回避策: それが望ましくない動作である場合は、単純にデフォルト値を関数本体に移動します。関数が実行されるたびにコード内の値が実行される限り、毎回新しいオブジェクトが取得されます:
>>> def saver(x=None):
... x が None の場合:
... x=[]
... x.append(1)
... print x
...
>> saver([2])
[2, 1]
>>> saver()
[1]
>>> saver()
[1]
>>> saver()
[ 1]
上記の if ステートメントは、代入 x=x または [] でほぼ置き換えることができます。これは、Python の or がそのオペランドの 1 つを返すためです。パラメーターが渡されない場合、x はデフォルトで None になるため、右側に or が返されます。生成された空のリスト。しかし、それはまったく同じではありません。空のリストが渡されると、関数は以前のバージョンのように渡されたリストを展開して返すのではなく、新しく生成されたリストを展開して返します (式は [] または [] となり、 を計算します)。新しいリスト)