Pythonの入門本で学習を進めている中で、関数内関数の存在意義がよくわからなかったので質問させてください(仕組みや挙動自体については理解できますが、積極的に使うシチュエーションがよくわからないという趣旨の質問です)
入門本にあるのは以下のようなソースコードなのですが
python
1def outer(a, b): 2 def inner(c, d): 3 return c + d 4 return inner(a, b)
シンプルに以下のように書くか、少し長めの関数であればわざわざ内部関数として定義せずに
通常の関数としてinnerも定義してouter関数内で使い回せばいいよう(そのほうが他の関数からも
参照できてコードの省略になりそう)な気がしてしまいます。
python
1def outer(a, b): 2 return a + b
繰り返しにはなりますが、どのような場合に関数内関数を使うメリットがあるか
ご教示願えますと幸いです。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答6件
0
① 外側の関数のローカル変数にも内側の関数からアクセス出来る
② 内側の関数名が外から見えず、他から使われないことが保証できるので読むとき楽
①+② ⇒ ある関数内でのみ必要な処理を気軽にコンパクトに書ける
②は、「ローカル変数の意義が分からない。全部グローバル変数でいいのでは?」というのに通じる話だと思います。
関数の外に出して独立した関数にすべきか、内部関数にすべきかは、関数の仕様を考えた時点で自然に決まると思います。
あとは、①の応用として、他の方の回答にあるクロージャーですね。クロージャーを使うためには、「外側の関数のローカル変数を参照できる内部関数」ないしそれ相当の機能が必要です。
投稿2019/05/06 15:13
編集2019/05/07 01:12総合スコア85766
0
Pythonでよく使われるシチュエーションは引数付きのデコレータでしょうか。
http://flask.pocoo.org/docs/1.0/quickstart/
に登場するデコレータapp.route
の実装、Flask
クラスのroute
メソッドを見ると関数内関数が登場します。
https://github.com/pallets/flask/blob/1.0.2/flask/app.py#L1224
「「引数として渡された値を束縛した関数」を返す関数」を実装するので関数内関数が自然ですね。
投稿2019/05/07 02:43
総合スコア11202
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
なんつーか、質問者さんに同感です。
まず、関数/サブルーチンてのは、機能を分割して
わかりやすいプログラムにするのが目的ですよね。
または、繰り返して使う処理を取り出して
関数/サブルーチンにするみたいなこともしますな。
翻って関数内で(関数内でしか使わない)同じ処理が
散見される場合は使い道があるようにも感じます。
ただ、その場合は機能分割を見直した方が
いいかもしれないという不安はあります。
ちなみに、自分が使う可能性があるとしたら、
処理で使用する関数のパラメタに関数を渡す時
くらいだと思いますが、無名関数が使えるようなら
その必要もないと考えます。
投稿2019/05/06 15:45
総合スコア7460
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/05/07 02:59
2019/05/07 14:40
2019/05/12 14:11 編集
0
呼び出し元のスコープにある幾つもの変数に依存するとき、というのがまっさきに思い浮かぶ活用例だと思います。
python
1def g(a, b, c, d, e): 2 print(a + b + c + d + e) 3 4def f(): 5 a, b, c, d, e = 1, 2, 3, 4, 5 6 g(a, b, c, d, e) 7 8def f(): 9 def g(): 10 print(a + b + c + d + e) 11 12 a, b, c, d, e = 1, 2, 3, 4, 5 13 g() 14
結果は同じですが、どちらが理にかなっているのかは言うまでもないでしょう。
投稿2019/05/06 14:09
総合スコア30935
0
ベストアンサー
内部関数を返し、クロージャを利用するのが良くある活用法です。
Python
1def make_who_say(who): 2 def who_say(sentence): 3 print(f'{who}: {sentence}') 4 5 return who_say 6 7 8tom_say = make_who_say('Tom') 9mary_say = make_who_say('Mary') 10 11tom_say('Hi, Mary.') 12mary_say('Hi, Tom.')
実行結果 Wandbox
plain
1Tom: Hi, Mary. 2Mary: Hi, Tom.
通常の関数としてinnerも定義してouter関数内で使い回せばいいよう(そのほうが他の関数からも
参照できてコードの省略になりそう)な気がしてしまいます。
おっしゃる通りで、他の方法で充分簡潔に代替できる際に積極的に用いることは無いでしょう。
追記
関数の可視性を制御するのはモジュールの役割であって、
それを内部関数で実現するのは本質的ではないように個人的には思います。
共通の変数にアクセスする関数が多くあるのなら、クラスを組めば良いでしょう。
関数内の変数に強く依存したコードを書くなら、コメントをしっかり付けてべた書きすれば良いでしょう。
何度も同じ処理をする必要があるのなら、それが関数内変数に依存するのは設計に問題がありそうです。
内部関数は高階関数を組むときを除いて積極的に使うものでは無いと、私はやはり思うのです。
投稿2019/05/06 13:56
編集2019/05/07 04:17総合スコア35668
0
外では使わない関数を内部で定義します。同じ名前の関数、この場合はinnerが複数のoutterにあっても良く、同じ名前を使った方が分かりやすい場合もあります。デコレータはどの関数でも同じ名前になり、その例と言えます。内部関数、class みたいな考え方ない場合、funcに対して、funcinit, funccopy_contractor みたいな名前を付け、しかもその名前がどこからでも見えるために、funcinitとfunc_init, funcinit_init の間の関係について悩むようなことになります。
投稿2019/05/14 01:45
総合スコア580
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/05/07 01:11 編集
2019/05/07 01:13
2019/05/12 14:08