北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

Pythonのクロージャの制限とその回避策

驚きました。LLが違えば挙動も違うもんですなあ。

ネストスコープの変数に対して代入・削除する機能は存在しない
Effective Python

うそーん? 試してみたら本当に駄目でした。

 def countfunc():
     i = 0
     def retfunc():
         i += 1
         return i
     return retfunc

f = countfunc()
print f()
print f()
print f()
Traceback (most recent call last):
  File "???.py", line ?, in <module>
    print f()
  File "???.py", line ?, in retfunc
    i += 1
UnboundLocalError: local variable 'i' referenced before assignment

ありゃまあ。ついでにぺらぺらページをめくってたら、次の解決策に辿り着きました。

要素が1つの配列をfinalな参照で保持すれば、クロージャで1つのローカル変数を参照する機能をエミュレートできる。内部クラスはその参照の値そのものを変えることはできないが、参照されている配列の要素の値は変えることができるからである。このテクニックはJavaに限ったものではなく、Pythonなど似た制限を持つ言語でも有効である。

wikipedia

ああ、そうか。言われてみればそうですね。

def countfunc():
    i = [0]
    def retfunc():
        i[0] += 1
        return i[0]
    return retfunc

f = countfunc()
print f()
print f()
print f()
1
2
3

でも、ダサイからかなり嫌。