質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.80%

関数内関数を積極的に使うシチュエーションがわからない・意義がいまいち理解できない

解決済

回答 6

投稿

  • 評価
  • クリップ 3
  • VIEW 1,491

komicool

score 24

Pythonの入門本で学習を進めている中で、関数内関数の存在意義がよくわからなかったので質問させてください(仕組みや挙動自体については理解できますが、積極的に使うシチュエーションがよくわからないという趣旨の質問です)

入門本にあるのは以下のようなソースコードなのですが

def outer(a, b):
    def inner(c, d):
        return c + d
    return inner(a, b)

シンプルに以下のように書くか、少し長めの関数であればわざわざ内部関数として定義せずに
通常の関数としてinnerも定義してouter関数内で使い回せばいいよう(そのほうが他の関数からも
参照できてコードの省略になりそう)な気がしてしまいます。

def outer(a, b):
    return a + b

繰り返しにはなりますが、どのような場合に関数内関数を使うメリットがあるか
ご教示願えますと幸いです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 6

+5

① 外側の関数のローカル変数にも内側の関数からアクセス出来る
② 内側の関数名が外から見えず、他から使われないことが保証できるので読むとき楽
①+② ⇒ ある関数内でのみ必要な処理を気軽にコンパクトに書ける

②は、「ローカル変数の意義が分からない。全部グローバル変数でいいのでは?」というのに通じる話だと思います。

関数の外に出して独立した関数にすべきか、内部関数にすべきかは、関数の仕様を考えた時点で自然に決まると思います。

あとは、①の応用として、他の方の回答にあるクロージャーですね。クロージャーを使うためには、「外側の関数のローカル変数を参照できる内部関数」ないしそれ相当の機能が必要です。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/07 00:49 編集

    3. Pythonの関数は第一級関数であるため外側の関数fのローカル関数gをfから別の関数へ渡したりfの戻り値に返したりできる。そのようにして用いられたgはたとえfがreturnした後であってもfのローカル変数をアクセスし続けられる。

    といった第三の説明を仮定しますと、自分はクロージャーの意味を、otnさんが挙げておられる1と上述した3を包含した概念として捉えています。そのため

    > あとは他の方の回答にあるクロージャーです

    の一文がちょっとひっかかりました。つまりotnさんの1は既にクロージャーの重要な性質を述べておられるような気がしたのです。いかがでしょうか?
    ---
    すみません、上のコメント取り消します。別に不自然さはないと思いなおしました。

    キャンセル

  • 2019/05/07 10:13

    ちょっと記述を修正しました。ついでに誤記を訂正。

    キャンセル

  • 2019/05/12 23:08

    大変簡明な回答でわかりやすかったです(BAにしようかとても迷いました)。ご回答ありがとうございました。

    キャンセル

+3

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/12 23:11

    参考になりました。Flaskはまだ触れていないのですが例として適当なものを紹介してくださり助かりました。デコレータは今後おりを見て使っていきたいと思います

    キャンセル

checkベストアンサー

+2

内部関数を返し、クロージャを利用するのが良くある活用法です。

def make_who_say(who):
    def who_say(sentence):
        print(f'{who}: {sentence}')

    return who_say


tom_say = make_who_say('Tom')
mary_say = make_who_say('Mary')

tom_say('Hi, Mary.')
mary_say('Hi, Tom.')

実行結果 Wandbox

Tom: Hi, Mary.
Mary: Hi, Tom.

通常の関数としてinnerも定義してouter関数内で使い回せばいいよう(そのほうが他の関数からも
参照できてコードの省略になりそう)な気がしてしまいます。

おっしゃる通りで、他の方法で充分簡潔に代替できる際に積極的に用いることは無いでしょう。

追記

関数の可視性を制御するのはモジュールの役割であって、
それを内部関数で実現するのは本質的ではないように個人的には思います。

共通の変数にアクセスする関数が多くあるのなら、クラスを組めば良いでしょう。
関数内の変数に強く依存したコードを書くなら、コメントをしっかり付けてべた書きすれば良いでしょう。
何度も同じ処理をする必要があるのなら、それが関数内変数に依存するのは設計に問題がありそうです。

内部関数は高階関数を組むときを除いて積極的に使うものでは無いと、私はやはり思うのです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/12 23:07 編集

    質問した後 学習を進めまして、クロージャ・デコレータでの利用が一番合理性があると自分でも感じました。一番最初に回答をくださったこと、回答が要領を得ていることからBAとさせていただきました。ありがとうございました。

    キャンセル

+2

呼び出し元のスコープにある幾つもの変数に依存するとき、というのがまっさきに思い浮かぶ活用例だと思います。

def g(a, b, c, d, e):
    print(a + b + c + d + e)

def f():
    a, b, c, d, e = 1, 2, 3, 4, 5
    g(a, b, c, d, e)

def f():
    def g():
        print(a + b + c + d + e)

    a, b, c, d, e = 1, 2, 3, 4, 5
    g()

結果は同じですが、どちらが理にかなっているのかは言うまでもないでしょう。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/12 23:07

    参考になりました。ご回答ありがとうございました!

    キャンセル

+2

なんつーか、質問者さんに同感です。

まず、関数/サブルーチンてのは、機能を分割して
わかりやすいプログラムにするのが目的ですよね。
または、繰り返して使う処理を取り出して
関数/サブルーチンにするみたいなこともしますな。

翻って関数内で(関数内でしか使わない)同じ処理が
散見される場合は使い道があるようにも感じます。
ただ、その場合は機能分割を見直した方が
いいかもしれないという不安はあります。

ちなみに、自分が使う可能性があるとしたら、
処理で使用する関数のパラメタに関数を渡す時
くらいだと思いますが、無名関数が使えるようなら
その必要もないと考えます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/07 11:59

    pythonのlambda式は縛りがきつい(単一の式しか書けない)ので、
    >無名関数が使えるようなら
    >その必要もないと考えます。
    は少し難しいかもしれません。

    キャンセル

  • 2019/05/07 23:40

    アドバイスありがとうございます。
    Python慣れてないのがバレバレですね。(汗

    キャンセル

  • 2019/05/12 23:10 編集

    回答ありがとうございます!私も質問時点では利用意義を見出せていなかったのですが、他の皆さんの回答を読んでデコレータでの利用は少しありだなと思い直しました

    キャンセル

0

外では使わない関数を内部で定義します。同じ名前の関数、この場合はinnerが複数のoutterにあっても良く、同じ名前を使った方が分かりやすい場合もあります。デコレータはどの関数でも同じ名前になり、その例と言えます。内部関数、class みたいな考え方ない場合、funcに対して、funcinit, funccopy_contractor みたいな名前を付け、しかもその名前がどこからでも見えるために、funcinitとfunc_init, funcinit_init の間の関係について悩むようなことになります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.80%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る