入れ子の関数(Python)

Google Python Style Guide の和訳をお昼休みにすこしずつ進めている。
今日は 2.6 節の入れ子のクラスと関数を訳していた。

ところでこの節、のっけに以下の記述がある。

fine when used to close over a local variable

close をどう訳すかという点で悩む。たぶん「ローカル変数を(クロージャ―として)閉じこむのはよい」だろうと考えている。

しかし Python のクロージャーはなかなかクセが強い。 global や nonlocal で明示的に指定しないかぎり、外のスコープの変数は「読める」けれど「更新できない」。(2.6.1 にも以下の注記がされているくらい)

Nested functions have read-only access to variables defined in enclosing scopes.

読み取り専用と考えると、クロージャーとしての使いどころはなんだろうか。(そして書きかえられるしても、クロージャーはどう使ったら「よい」と感じられるだろう?)

* * *

クロージャーと聞いて、まず浮かぶのはカウンターをつくる例。

def mk_counter(starts_with: int = 0):
  def incr():
    nonlocal starts_with
    t = starts_with
    starts_with += 1
    return t
  return incr

指定した数で数えはじめるカウンター「関数」をつくって返す「高階関数」。

こんな感じに使える:

>>> c = mk_counter()
>>> c()
0
>>> c()
1
>>> c()
2
>>> d = mk_counter(100)
>>> d()
100
>>> c()
3
>>> d()
101
>>> c()
4

ただ Python には generator がある。カウンターをつくるならたぶん generator のほうがわかりやすい。

def mk_counter_gen(starts_with: int = 0):
  while True:
    yield starts_with
    starts_with += 1

yield を呼ぶ関数は next 関数を経由して次の値を読みだせる。(関数末尾に到達すると StopIteration 例外が送出される。これは iterator に対して for ループを回すときと同じインターフェース)

>>> e = mk_counter_gen()
>>> next(e)
0
>>> next(e)
1
>>> next(e)
2

* * *

戻って。

Python における「クロージャー」の使いどころがわからない。(Python でなくても、わからない)

この記事が気に入ったらサポートをしてみませんか?