クロージャーの作成
最初のCODE
Jupyterlabを使っていつも遊んでいます。
今回はカウンターをクロージャーを使って作ってみようと思います。
def makeCounter(a): b = a def inner(): b += 1 return b return inner counter = makeCounter(10) print(counter()) print(counter()) print(counter())
もののみごとに弾かれました。print(counter())がエラーとなってますので、counter()としてみましたが変わりませんでした。b += 1 にもエラーが出ているので b = 1 として、変数 b の ID を見てみることにしました。
Pythonのすべてのオブジェクトには独自の一意のIDがあり、id()関数は指定されたオブジェクトのIDを返します。
IDはオブジェクトのアドレスでオブジェクトの作成時に割り当てられ、プログラムを実行するたびごとに異なります。
点検に使ったCODE
def makeCounter(a): b = a print(id(b)) def inner(): b = 1 print(id(b)) return b return inner counter = makeCounter(10) counter() counter() counter()
外部関数の変数 b と内部関数の中の変数 b が違っていることが分かりました。これでは、内部関数の中の変数 b が初期化されていないため加算もできません。
解決したCODE
def makeCounter(a): b = [a] print(id(b)) def inner(): b[0] += 1 print(id(b)) print(b[0]) return b[0] return inner counter = makeCounter(10) counter() counter() counter()
変数 b を参照系のリストにしてみました。外部関数・内部関数の変数 b のIDは一致して counter を呼び出す度に 1 づつ加算されて目的が果たされています。
最終形
不必要なところは取り除いて最終形はこのようになりました。
def makeCounter(a): b = [a] def inner(): b[0] += 1 return b[0] return inner counter = makeCounter(10) print(counter()) print(counter()) print(counter())
考察
class Count: def __init__(self, realpart): self.r = realpart def makeCounter(a): nextval = Count(a) def inner(): nextval.r += 1 return nextval.r return inner counter = makeCounter(10) print(counter()) print(counter()) print(counter())
変数 b が参照系ならよさそうなので他にどのような型できるのか考察してみました。
出てきた考えが class を使ってみることで、実行してみました。うまく機能しているようです。