バリケンのPython日記 RSSフィード

2008-07-10

[] 関数(5)  関数(5) - バリケンのPython日記 を含むブックマーク はてなブックマーク -  関数(5) - バリケンのPython日記  関数(5) - バリケンのPython日記 のブックマークコメント

クロージャ

昨日の日記で、Pythonでは関数定義の仮引数デフォルト式は「関数が定義された時点で」「関数を定義している側のスコープ(scope)で」評価される、ということがわかったよね。

つまり、これってクロージャだよね。デフォルト式は「関数が定義された時点で」評価され、そして仮引数は自由変数の扱いになるから、デフォルト値として変数に代入した値は(呼出しごとにクリアされたりせず)関数定義後も関数からの参照が保持されるよ。

これはクロージャをちゃんと理解していれば便利だけど、知らないとハマりそう。クロージャについては以前「Rubyの「クロージャ」再考」というエントリを書いたから、参考にしてね。

じゃあ、ちょっと実験してみよう!

>>> def f(a, L=[]):
...   L.append(a)
...   return L
...
>>> print f(1)
[1]
>>> print f(2)
[1, 2]
>>> print f(3)
[1, 2, 3]
>>>

デフォルト値を保持せずに関数呼び出しごとにクリアしたいというときは、次のように関数内部のスコープで代入するようにすればいいよね。

>>> def f(a, L=None):
...   if L is None:
...     L = []
...   L.append(a)
...   return L
...
>>> print f(1)
[1]
>>> print f(2)
[2]
>>> print f(3)
[3]
>>>

ちなみにRubyで同様のクロージャを実現したいときは、Procオブジェクトを使うよ。ProcオブジェクトはProc.newまたはprocメソッド、もしくはlambdaメソッドで生成できるよ。

irb(main):007:0> def make_f
irb(main):008:1>   l = []
irb(main):009:1>   return lambda {|a| l.push(a) }
irb(main):010:1> end
=> nil
irb(main):011:0* f = make_f
=> #<Proc:0x02b2ccd8@(irb):9>
irb(main):012:0>
irb(main):013:0* p f.call(1)
[1]
=> nil
irb(main):014:0> p f.call(2)
[1, 2]
=> nil
irb(main):015:0> p f.call(3)
[1, 2, 3]
=> nil
irb(main):016:0>
トラックバック - http://python.g.hatena.ne.jp/muscovyduck/20080710