- ベストアンサー
Pythonの関数内での関数の定義について
- Python Cookbook 3rdの2章6節で説明されているサンプルコードでは、matchcase関数内のreplace関数に引数を渡していないことが疑問です。
- サンプルコードの実行分で、re.sub(...)によってマッチした情報がreplace(m)の引数mに渡される仕組みになっているのでしょうか?
- なぜreplace関数に引数を指定せずに、re.sub(...)でのマッチ情報が渡されるのかが理解できません。
- みんなの回答 (1)
- 専門家の回答
質問者が選んだベストアンサー
あー、うん、どう言えばいいんだろ(笑)。 Lisp経験者とかなら「あー、これは関数を返す関数だから」とかすぐ分かるんですが(笑)。 最近よく出る(例えばJava8で出て来た新機能)、「クロージャ」ってのがこれなんです。 つまり、関数matchcaseってのは中で定義された「ローカル関数」replace、ってのを最後に「丸ごと」返してますよね(return replace)。これはmatchcaseが「関数を返す関数だ」って事なんです。 関数が計算した値を返してるわけじゃない、関数「そのものを」返してるんです。大事な事なんで二回言いました。 ちょっと簡単な例を挙げてみましょう。次のような関数を定義してみます。 def foo(x): def bar(y): return x * y return bar 関数fooは引数xを一つ取ります。内部にローカル関数barを定義してて、barは「xとは全く関係ない」別の引数yを取ります。つまり、ローカル関数barはfooが求める引数xを「引数としては使わない」わけですよね。全く別の引数yを求めていて、しかもbarはxとyの積を計算します。 実際、fooに2つの引数、例えば2×3を計算させたいとして、2と3を与えるとエラーを返すわけです。 >>> foo(2, 3) Traceback (most recent call last): File "<pyshell#19>", line 1, in <module> foo(2, 3) TypeError: foo() takes 1 positional argument but 2 were given 当然一つの引数しかfooは取れないので、ここでは2を与えてみます。 >>> foo(2) <function foo.<locals>.bar at 0x027E2B28> fooは「関数を返す関数」なんで、なんかの計算した値は返してくれません。その代わり変わったリテラルを返してきます。日本語で言うと要するに 「関数fooはローカル定義された関数barを返しました」 的な一種のメッセージを返してくるわけです。これが実はクロージャなんですね。こう言う表現で返してくる。 さて、じゃあ適当に、変数varにfoo(2)を代入してみましょう。 >>> var = foo(2) >>> 何も返ってきませんね。まあ、これは単純にvarに代入しただけなんで当然です。 varの中身を見てみましょうか。 >>> var <function foo.<locals>.bar at 0x027E2B70> はい、さっきと同じような「ローカル関数barですよ」と言う一種のメッセージが返ってきました。 さて、これがクロージャの面白いトコなんですが、varは変数です。ただし、中に入ってる「値」ってのはどうやら「barと言うfoo内部のローカル関数らしい」って事が分かっています。つまり、原理的にはvarは「関数と言う機能を持ってるんじゃないか」って事なんですよ。あれ? 実はこの推論は正しく、「クロージャを代入された変数varは関数として振る舞う」んです。var自体は変数なのに?実はそうなんですね。 ここで、変数varに引数3を与えてみます。「動く筈ねぇじゃねぇか」って思ってもさにあらず。 >>> var(3) 6 なんと、変数varに「引数3を与えたら」6が返ってきました。ビックリですね(笑)。 つまり、どういう事か、と言うと、関数fooは引数を一つ取って、ローカル関数barを返す関数として定義しました。変数varは「引数2を取った関数foo」を保持したままの(言い換えると引数2を取った状態でローカル関数bar自体を剥き出しにした)状態です。今、変数varはつまり、「引数待ちのbar」、言い換えると、一種計算途中のまま「待機状態」だったわけで、そこに新しく「barに与える為の」引数をvarを介して与えると「止まってた計算が動き出す」ようなカンジになるんですね。 これがクロージャ、で、今、JavaScript等の比較的新しい言語には搭載されてる機能です。 ついでに言うと、最近Java8にも入ってきて、多くの古典的なJavaユーザーを混乱に叩き込んでる機能です(笑)。
お礼
返事が遅くなり申し訳ございません。 どうも、ありがとうございます。 大変ご丁寧な解説で、すぐに理解することができました。 回答者様がいなければ、この謎を解くために数時間浪費して、最後には爆発していたと思います。本当にありがとうございます!!!