ホームページ >バックエンド開発 >Python チュートリアル >Python 上級: 状態を保持するクロージャ
クロージャ
Python では、関数もオブジェクトです。したがって、関数を定義するときは、入れ子になった関数を定義して、入れ子になった関数を返すことができます。たとえば:
from math import pow def make_pow(n): def inner_func(x): # 嵌套定义了 inner_func return pow(x, n) # 注意这里引用了外部函数的 n return inner_func # 返回 inner_func
上記のコードでは、関数 make_pow は内部関数 inner_func を定義し、その関数を返します。したがって、make_pow を使用して別の関数を生成できます:
>> > pow2 = make_pow(2) # pow2 是一个函数,参数 2 是一个自由变量 >> > pow2 <function inner_func at 0x10271faa0 > >> > pow2(6) 36.0
また、内部関数 inner_func が外部関数 make_pow の自由変数 n を参照していることにも気付きました。つまり、関数 make_pow のライフサイクルが終了すると、n はこの関数になります。変数は引き続き inner_func によって参照される inner_func に保存されます。
>> > del make_pow # 删除 make_pow >> > pow3 = make_pow(3) Traceback(most recent call last): File "<stdin>", line 1, in < module > NameError: name 'make_pow' is not defined >> > pow2(9) # pow2 仍可正常调用,自由变量 2 仍保存在 pow2 中 81.0
---|---
上記の状況と同様に、関数は外部関数の関連するパラメータと変数を参照する内部関数を返します。返された内部関数をクロージャ (Closure) と呼びます。
上記の例では、 inner_func は自由変数 n を参照するクロージャです。
クロージャの役割
クロージャの最大の特徴は、クロージャを生成した環境が解放されても、クロージャは依然として存在することです
クロージャは複数のインスタンスを持つことができます。渡されるパラメータが次のように同じであっても、クロージャを使用してクラスのインスタンスをシミュレートすることもできます。
>> > pow_a = make_pow(2) >> > pow_b = make_pow(2) >> > pow_a == pow_b False
クロージャを使用して実装します:
from math import sqrt class Point(object): def __init__(self, x, y): self.x, self.y = x, y def get_distance(self, u, v): distance = sqrt((self.x - u) ** 2 + (self.y - v) ** 2) return distance >> > pt = Point(7, 2) # 创建一个点 >> > pt.get_distance(10, 6) # 求到另一个点的距离 5.0
クロージャの概念は非常に単純ですが、次の例のように、その実装においていくつかの誤解が生じやすいです:
def point(x, y): def get_distance(u, v): return sqrt((x - u) ** 2 + (y - v) ** 2) return get_distance >> > pt = point(7, 2) >> > pt(10, 6) 5.0
この例では、各 for ループに関数を作成し、それを追加します。関数に。さて、上記の関数を呼び出すと、返される結果は 1、2、3 であると思うかもしれませんが、実際はそうではありません:
def count(): funcs = [] for i in [1, 2, 3]: def f(): return i funcs.append(f) return funcs
なぜですか?その理由は、上記の関数 f は変数 i を参照していますが、for ループが終了した時点で関数 f はすぐには実行されず、変数 i の値は 3 であり、func 内の関数が参照する変数はすべて 3 であるためです。 、最終結果はオール3です。
したがって、クロージャ内のループ変数、または後で変更される変数の参照は避けるようにする必要があります。
それでは、上記の状況をどのように解決すればよいでしょうか?次のように、別の関数を作成し、その関数にループ変数の値を渡すことができます:
>> > f1, f2, f3 = count() >> > f1() 3 >> > f2() 3 >> > f3() 3
概要
クロージャは、作成した外部関数のライフサイクルであっても、自由変数を運ぶ関数です。クロージャが終了しても、クロージャによって参照される自由変数はまだ存在します。