入れ子関数は、コードを整理し、より広いコンテキスト内で機能をカプセル化する便利な方法を提供します。ただし、ネストされたスコープ内で変数がどのように処理されるかを理解すると、開発者は困惑することがよくあります。
次のコード スニペットを考えてみましょう。
class Cage(object): def __init__(self, animal): self.animal = animal def get_petters(): for animal in ['cow', 'dog', 'cat']: cage = Cage(animal) def pet_function(): print("Mary pets the " + cage.animal + ".") yield (animal, cage.animal)
この例では、ジェネレーター関数 get_petters() が動物のリスト。動物ごとに Cage オブジェクトを作成し、動物の名前とケージのローカルへのアクセスを試みる入れ子関数を含むタプルを生成します。 variable.
このコードを実行すると、ケージ変数の 3 つの異なるインスタンスに対応する 3 つの異なる動物が出力されることが期待されるかもしれません。ただし、出力では「Marypets the cat」のみが繰り返し生成されます。
問題の核心は、Python におけるクロージャの性質にあります。ネストされた関数が定義されると、それを囲んでいるスコープ内の変数への参照がキャプチャされます。提供されたコードでは、pet_function は get_petters() 関数内にネストされているため、ケージ変数にアクセスできます。
ただし、この参照は関数定義時には確立されません。代わりに、関数の実行時に発生します。ネストされた関数が実行されるまでに、動物のリストを反復処理するときに、ケージ変数にはすでに値「cat」が割り当てられています。
この問題を解決するには、次の方法があります。いくつかのアプローチを採用します。
1.部分関数:
部分関数は、既存の関数をラップし、その引数の一部を事前設定された値で初期化する呼び出し可能関数です。この場合、 functools.partial() を使用して、ケージ変数を適切なコンテキストにバインドする部分ペット関数を作成できます:
def pet_function(cage=None): print("Mary pets the " + cage.animal + ".") yield (animal, partial(pet_function, cage=cage))
2。新しいスコープの作成:
もう 1 つのオプションは、ケージ変数が常にローカルで正しい値にバインドされるようにする、ネストされたスコープ内でペット関数を定義することです:
def scoped_cage(cage=None): def pet_function(): print("Mary pets the " + cage.animal + ".") return pet_function yield (animal, partial(pet_function, cage))
3.デフォルトのキーワード パラメータ:
ケージ変数をデフォルトのキーワード引数としてペット関数に渡すこともできます:
def pet_function(cage=cage): print("Mary pets the " + cage.animal + ".") yield (animal, partial(pet_function))
これらの手法に従うことで、ネストされた関数は予期されるローカル変数を使用して動作するため、予期しない副作用が排除され、コードの明瞭さが維持されます。
以上がPython の入れ子関数がループ変数の最後の値にのみアクセスするのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。