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

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

新規登録して質問してみよう
ただいま回答率
85.35%
パラメータ

関数やプログラム実行時に与える設定値をパラメータと呼びます。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

文字コード

文字コードとは、文字や記号をコンピュータ上で使用するために用いられるバイト表現を指します。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

1088閲覧

pythonのクロージャーと関数内関数について

fL8kZjyKQi2NEst

総合スコア7

パラメータ

関数やプログラム実行時に与える設定値をパラメータと呼びます。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

文字コード

文字コードとは、文字や記号をコンピュータ上で使用するために用いられるバイト表現を指します。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/05/16 21:17

編集2020/05/17 13:49

python

1def add_sound(stroke): 2 def inner(): 3 return stroke + '-->パコーン' 4 return inner 5a = add_sound('バックハンドストローク')

秀和システムのpythonプログラミングパーフェクトマスターに載ってるコードを書きました。クロージャーを学んだのですが、これを実行するときに

python

1a()

と入力することでクロージャーが実行できるとのことでした。このa()の()はなんなのですか?
aの時は「バックハンドストローク」というパラメータが既に入っているから()のなかは空欄でいいのか?と思い、a('スマッシュ')と入力したのですが、

python

1Traceback (most recent call last): 2 File "<pyshell#16>", line 1, in <module> 3 a('スマッシュ') 4TypeError: inner() takes 0 positional arguments but 1 was given

とエラーが出ました。
このa()の()は何を意味しているのでしょうか?
関数内関数と一緒に記されていて

python

1def add_sound(stroke): 2 def inner(s): 3 return s + 'パコーン' 4 return inner(stroke) 5a=add_sound('フォアハンドストローク')

に対して

python

1a

で実行できました。
使用用途の違いについてや、クロージャーでのa()の()の意味について教えてください。

追記
a()のかっこについて、__call__を呼び出しているに過ぎないということがわかりました
引用元
object.call(self[, args...])
インスタンスが関数として "呼ばれた" 際に呼び出されます; このメソッドが定義されている場合、 x(arg1, arg2, ...) は x.call(arg1, arg2, ...) を短く書いたものになります。
pythonサイト
少し難しいのですが

python

1a('スマッシュ')

で実行できないことについて、決まった引数でしか実行できないのでしょうか?

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
今回の私の質問について私の解釈を記します。

python

1def add_sound(stroke): 2 def inner(): 3 return stroke + '-->パコーン' 4 return inner 5a = add_sound('バックハンドストローク')

について、a はこのコードの4行目return innerからinnerという関数を得ているから

python

1type(a) 2>>>class 'function'

と帰ってきている。
もう一つ、同じテキストで次のようなコードが載っていたので、試してみました。

python

1def add_sound2(attack): 2 for i in range(5): 3 print(attack,'--> パッコーン') 4 return attack 5 6b = add_sound2('スマッシュ') 7 8type(b) 9>>>class 'str' 10 11b 12>>>'スマッシュ' 13b() 14>>>TypeError: 'str' object is not callable

bという変数では引数が'スマッシュ’であるadd_sound2()という関数に結びついていて、return attackよりattackが戻り値として帰ってきている。attackという変数についてadd_sound2を実行する際に'スマッシュ'というstrが入れられているため、bを参照すると'スマッシュ'が表示された。
今回テーマのクロージャではinnerという関数を返していることで同じ処理をa()という形で何度も処理できるということですね

ほんとに何度も追記質問を繰り返してすみませんでした。非常に理解が深まりました!!

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

typeでaを確認すると分かります。
一つ目は関数のinnerそのものが返されます。typeで見るとfunctionです
二つ目はinner(フォアハンドストローク)を実行した結果のreturnが返されます。typeはstrです。

innerそのものは語弊があるかもしれないけれど、自身で書かれたコードのreturn見れば分かりますよね?

投稿2020/05/16 21:30

編集2020/05/16 21:36
hentaiman

総合スコア6426

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fL8kZjyKQi2NEst

2020/05/16 21:45

関数内関数では実行結果を返していて、クロージャーでは関数を返しているのですね。使用用途についてはどのように違ってくるのでしょうか? クロージャーの優れたところとして、グローバル変数を設定しなくていい。同じ処理を何度もできるとあるのですが、関数内関数であっても同じだと思います、、、 また、今から追記することについても良かったら教えてください。
hentaiman

2020/05/16 23:25

関数内関数が実行結果を返しているのではなく、実行結果を返すようにコードを書いているからそうなっているだけです。クロージャも同様です。関数を返すものをクロージャと言っているわけではありません。 回答に書いてある意味そのままですが、 `a('スマッシュ')`が出来ないのは`return inner(stroke)`を返してるからで、aには既にinner(stroke)を実行した結果が入っています `return inner`にすると好きな引数を使えるようになりますが、それは`inner関数自体`がaに入っているからです 同じ処理を何度も出来るの意味が分かりませんが、その文章が書いてある前後の文脈も併せて読めば多分ちゃんと理解出来る内容になってるんじゃないですか? 多分変数が初期化される事なく使える的な内容なんじゃないかと思いますが・・・ グローバル変数を設定しなくていいという説明もおかしいので、「クロージャ スコープ」辺りで検索してみてください
fL8kZjyKQi2NEst

2020/05/17 08:58

私の認識がずれていたらすみません。一番最初のコードに対して、a('スマッシュ')と実行した時に3つ目のinner() takes 0 positional argument but 1 was given と出ています。inner()に1つの位置引数を与えているが、受け取れるのは0個だと言う意味だと思うのですが、教えていただいたことから考えると、aにはreturn innerのinnerが返されてるということでいいのでしょうか? そして、def inner()には引数が設定されてないからa('スマッシュ')のスマッシュが受け取れないということでいいのでしょうか?
hentaiman

2020/05/17 10:11

そうですね defの中にあるdefという形式的な部分を意識しすぎていて基礎的な関数の使い方に混乱が起きてるだけじゃないですか? def inner(): pass を inner(123)として使おうとしてもエラーになる事は分かりますよね?
fL8kZjyKQi2NEst

2020/05/17 13:52

何度も質問に乗っていただいてありがとうございました。最後の追記の解釈で間違い無いなら今回の追記で質問を終了したいと思います。本当にありがとうございました!!
guest

0

直接の回答じゃないですが。

"クロージャ"は関数閉包と訳される概念で、"引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決すること"という特徴を持った関数オブジェクトを指します。

Pythonでは関数内関数やラムダ式が"クロージャになれる仕様になっている"と捉える方がいいでしょう。


質問のソース

python

1def add_sound(stroke): 2 def inner(): 3 return stroke + '-->パコーン' 4 return inner 5a = add_sound('バックハンドストローク')

では、関数内関数innerがクロージャになっている(=引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決しているような関数オブジェクトである)わけです。

もっと精確に言うと"ローカル変数innerを束縛している関数オブジェクト"がクロージャになっているわけです。

"その関数オブジェクトをreturnで返しているかどうか"は"その関数オブジェクトかクロージャかどうか"とは関係がない話です。


と入力することでクロージャーが実行できるとのことでした。このa()の()はなんなのですか?

関数オブジェクトだから()で実行できるというだけの話です。


(追記)

質問者さんが理解していない事柄は

pytton

1def hoge(): 2 print('hoge')

という文を実行した時に何が起こるのか? という点だと想像します。

def文を実行すると

  • 与えられた引数リストとコードブロックから関数オブジェクトを生成する
  • 名前空間で関数名を関数オブジェクトに束縛する

の2つのことが起こります

上記の2番目で起こることは(単純)代入文と起こることと何ら変わりありません

言い換えると、def hoge(): print('hoge')を実行した時には、hoge = 関数オブジェクトを実行するのと同じことが起こっているのです。

その2つで同じように名前束縛が起こるのであれば、

python

1def hoge(): 2 print('hoge') 3 4hoge()

がまったく正しいPythonのコードで、hoge()で関数オブジェクトの呼び出しが起こることになんら不思議さを感じないと言うなら、それと同じよう

python

1hoge = 何か関数オブジェクト 2 3hoge()

まったく正しいPythonのコードであって、そこに不思議さを感じることがおかしいのです。

python

1def hoge(): 2 print('HOGE') 3 4print(hoge) # => <function hoge at 0xXXXXXXXX> (名前hogeを束縛しているオブジェクトが関数オブジェクトだと分かる) 5 6a = hoge # 名前hogeを束縛しているオブジェクトで名前aを束縛する 7 8hoge() # => HOGE (名前hogeを束縛しているオブジェクト=hoge関数を引数なしで呼び出す) 9a() # => HOGE (名前aを束縛しているオブジェクト=hoge関数を引数なしで呼び出す)

というコードを実行して考えてみてください。

名前の束縛の項もぜひ読んでみましょう。
https://docs.python.org/ja/3/reference/executionmodel.html#naming-and-binding

以下の構造で、名前が束縛されます:

  • 関数の仮引数 (formal parameter) 指定
  • import 文
  • クラスや関数の定義 (定義を行ったブロックで、クラスや関数名を束縛します)
  • 代入が行われるときの代入対象の識別子
  • for ループのヘッダ
  • with 文や except 節の as の後ろ。

と書いてあります。(わかりやすいように箇条書きにしています)

投稿2020/05/17 00:56

編集2020/05/17 10:17
quickquip

総合スコア11235

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fL8kZjyKQi2NEst

2020/05/17 08:11

自分の理解力がないのであれば、すみません。 a=add_sound('バックハンドストローク') と定義しているのであれば、関数オブジェクトのaには既に('バックハンドストローク')が含まれているので()が重複しているのではないかと思ってしまします。システム上こう言うことになっていると言われて仕舞えばそれまでなのですが、実行する時に()をつける意味として、いまいち理解できていません。 innerがその関数内のみの環境で解決しようとしている関数オブジェクトでありクロージャとはその環境の中で決まった処理を行うことができるシステムであることと理解しました。わかりやすくおしえていただいてありがとうございます。
quickquip

2020/05/18 00:10

a=add_sound('バックハンドストローク') と名前aをいったん束縛するのではなくて add_sound('バックハンドストローク')() が実行可能である理屈を考察する方が近道な気がします。
fL8kZjyKQi2NEst

2020/05/18 08:37

ありがとうございます。プログラミングを0から始めている私にとって、やっぱり公式サイトはちょっとハードルが高くて、一度読んでみたのですが制御フロー?ヘッダー?スイート?とちょっと調べながらじゃ無いと読めませんでした。故に時間がかかってしまって申し訳ないです。 しかし、やはり公式サイトには重要なことが書かれていますね・・・・頑張って読み深めていきます・・・・
guest

0

使いどきはいつか?ということですね。

関数内関数では実行結果を返していて、クロージャーでは関数を返しているのですね。使用用途についてはどのように違ってくるのでしょうか?

1. ある関数の中でしか使わない関数を定義するとき

絶対にとは言えませんが、ある関数の中でしか使わない関数を定義するときは、関数の中で関数を定義しても良いのかなと思います。

関数の外で関数を定義した場合、「その関数は他の関数でも呼ばれるのかな?」と読む側に誤解を与えます。それに対して、関数の中で関数を定義した場合、「その関数は他の関数で呼ばれることはない」ことが読む側にわかります。

どこからでも参照できる変数、あるいは関数は、精神的な負担が高くなることが多い気がします。そのため一般に、グローバルスコープではなく、ローカルスコープを使いましょう、とよく言われています。

2. クラスを定義するのが面倒なとき

関数内関数と nonlocal を組み合わせることで、クラス定義文を使うことなく、クラス定義文と同じようなことができます。ごく限られた場合ですが、クラス定義文より短く簡潔にかけることがあります。

投稿2020/05/16 23:49

編集2020/05/16 23:53
nico25

総合スコア830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fL8kZjyKQi2NEst

2020/05/17 08:39

使い方教えていただいてありがとうございます。紹介してくださったサイトが特にわかりやすかったです。 使うべき場面については、カリー化やデコレータとわからないので理解に至ってませんが、理解が深まりました!
nico25

2020/05/17 08:41

ありがとうございます :)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問