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

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

新規登録して質問してみよう
ただいま回答率
85.35%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Q&A

解決済

4回答

504閲覧

関数内関数の挙動とその中での引数の挙動が分からない

esklia

総合スコア81

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

0グッド

0クリップ

投稿2020/02/11 03:28

入門python3p122からです。

➀関数内関数の挙動なのですが、自分の理解は
「kの実引数'NI!'はkの仮引数sayingにコピーされた後、iの仮引数quoteにコピーされ、%sに埋め込まれ返される」というところまでできています(間違っているかもしれませんが…)
この後はi(saying)となっていますが、iの仮引数はquoteなのでなぜ機能するのかが不明です。いったいこの関数はどのように機能しているのでしょうか(自分の理解度的に質問が少し抽象的になってしまいすみません。)

➁入門python3ではそもそも「実引数が仮引数にコピーされる」というような表現がされていましたが、いまいちピンと来ていないので理解があいまいなままです。「実引数が仮引数にコピーされる」とはどういうことでしょうか?
以上二点よろしくお願いいたします。

def k(saying): def i(quote): return "We are the knights who say: '%s'" % quote return i(saying) >>> k('NI!') "We are the knights who say: 'NI!'"

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

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

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

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

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

guest

回答4

0

ベストアンサー

関数内関数といってもこの場合はただの関数とみなしていいのはLouiS0616さんの回答の通りです。

まず関数の概念から復習するといいでしょう。


kの実引数'NI!'はkの仮引数sayingにコピーされた後、

ここまではとりあえず良いと思いますが、

iの仮引数quoteにコピーされ、%sに埋め込まれ返される

という理解はたぶん間違っている気がします。後ろに

この後はi(saying)となっていますが

と書いてあるのが「正しくないんじゃないか」と判断した根拠です。「実引数が仮引数にコピーされる」のはi(saying)が呼び出された後です。

投稿2020/02/11 04:13

hayataka2049

総合スコア30935

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

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

esklia

2020/02/11 14:40

ご回答くださりありがとうございます。 ➀def k(saying)でkに'NI!'という文字列が渡り、 ➁その次の行で'NI!'がiの仮引数quoteに渡り、"We are the knights who say: 'NI!'"という文字列が返され、 ➂最後のi(saying)は何をしているのかが分からないのですが、ご説明頂いてもよろしいでしょうか? お手数をおかけします。
hayataka2049

2020/02/11 22:55

関数定義と関数呼び出しは別物ということがまだ理解できていないのでは? 定義は実質的な実行には寄与していないと捉えれば、先に「最後の」i(saying)が呼び出され、そこで「sayingが関数内関数の引数のquoteにコピーされる」と理解しないといけないはずです。
esklia

2020/02/15 10:07

恥ずかしながら関数呼び出しということに気づきませんでした。pythonは上から下にコードが実行されると認識していたのですが、  関数内関数では(語弊があるかもしれませんが、この場合では)先に関数の呼び出しの行、つまりreturn i(saying)が呼び出され、その時にdef i(quote)が参照?されてquoteの中に入っているNI!の文字列がsayingにコピーされ、returnされる という理解であっていますでしょうか。お手数をおかけして申し訳ございませんが、ご回答いただければと思います。
hayataka2049

2020/02/16 00:27

> quoteの中に入っているNI!の文字列がsayingにコピーされ、returnされる 逆です。強いて言えばsaying→quoteとコピーされます。 def f(arg): print(arg) f("hoge") はfの関数定義を通った後f("hoge")が呼び出されて関数定義のところに書いたprintが実行されるのでは?
esklia

2020/02/16 08:39

色々とやり取りが長くなったので再掲します。 def k(saying): def i(quote): return "We are the knights who say: '%s'" % quote return i(saying) >>> k('NI!') "We are the knights who say: 'NI!'" def f(arg): print(arg)についてはわかるのですが、def i(quote)の後の一番最後の行がなぜi(saying)となっているのかが理解できません。i(quote)中には既にNI!が値として与えられているのですから、最後の行もreturn i(quote)とした方が分かりやすいですし、そもそも三行目でreturnされているのに再度次の行でreturnする必要ってあるのですか?
hayataka2049

2020/02/16 08:52

> def i(quote)の後の一番最後の行がなぜi(saying)となっているのか 関数kのスコープ内にquoteはない > i(quote)中には既にNI!が値として与えられているのですから、最後の行もreturn i(quote)とした方が分かりやすい quoteはkのスコープ内にないのでそんな変数はないというエラーになります > そもそも三行目でreturnされているのに再度次の行でreturnする必要ってあるのですか? iのreturnとkのreturnなので両者は別物です。 察するに、やはり基礎的な部分から誤解されているような……
hayataka2049

2020/02/16 08:55

LouiS0616さんの回答にもありますが、今回のコードでは関数内関数であることは実質的な動作に影響していません。 LouiS0616さんの回答のコードがわかるならそれと基本的に同じ動作をしていると理解していただければよいです(iを定義する場所が違うだけ)。
esklia

2020/02/16 09:55

>iのreturnとkのreturnなので両者は別物です。 i関数がk関数の内部にあるのでkが呼び出されるとその中にあるiも呼び出されると誤解してました。 >quoteはkのスコープ内にないのでそんな変数はないというエラーになります こちらもk(saying)には"NI"が格納(代入)されていてNIを引き継ぐにはi(saying)とする必要があるということで理解できました。 稚拙な質問にもかかわらず付き合っていただきありがとうございます。概ね理解できたと思います。
hayataka2049

2020/02/16 11:46

> i関数がk関数の内部にあるのでkが呼び出されるとその中にあるiも呼び出されると誤解してました。 この点が根本的な誤解だったのかと思います。そんな魔法っぽいことは起こらないのが実態です。 kの中ではsaying = "NI!"されている感じになっていて、なのでi(saying)を呼び出す(i("NI!")と同じ結果になる)というだけです。
esklia

2020/02/16 14:29

早くから気づくには関数の挙動をもっと注意深く自分でもいじくる必要があったと思います。 皆さんもそうですが、親切丁寧に教えてくださり心から感謝申し上げます。
guest

0

少なくともご提示のコードについては、関数内関数であることはあまり動作に影響していないです。
次のように書いても同じような結果になる筈。

Python

1def i(quote): 2 return "We are the knights who say: '%s'" % quote 3 4def k(saying): 5 return i(saying) 6 7>>> k('NI!') 8"We are the knights who say: 'NI!'"

さて、改めて分からない点はあるでしょうか。

投稿2020/02/11 03:30

LouiS0616

総合スコア35668

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

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

esklia

2020/02/11 03:36 編集

ご回答くださりありがとうございます。 ➀k関数の中身が`returni(quote)なら`わかるのですが、仮引数がsayingになっている意味が分かりません。 ➁基本的な質問になってしまい恐縮ですが、実引数を渡したときに「実引数が仮引数にコピーされる」とはどういうことかがしっくり来れば納得できそうなので、そこを教えていただきたいです。
LouiS0616

2020/02/11 03:40 編集

参照の概念が絡んでくるので簡単に説明するのは難しいのですが、 入門レベルであれば『仮引数に新しい値を代入(束縛)しても実引数には影響しない』と捉えておけば良いでしょう。 ともすれば『別の変数名でも問題無いですよ』くらいのアバウトな表現なのかもしれませんが。 --- 註1: 引数は値なので、変数のレイヤで考えるのはややミスリードな気もする。でも実変数とか普通は言いませんものね。 註2: 仮引数への操作が呼び出し元に影響することもあります。この辺りの違いは慣れるまではいまいち分かりづらいので、『代入は影響しません』と分かり易い部分だけ書いています。
esklia

2020/02/11 14:35 編集

「実引数の値」で上書きされるので仮引数がどんな文字列であってもさほど影響しないという風に捉えました。ご教授ありがとうございます。
LouiS0616

2020/02/11 03:42

def func(arg): arg = 42 value = 0 func(value) print(value) # => 0 とりあえずこれだけ押さえておいて下さい。
esklia

2020/02/11 03:47 編集

すみません。見たことがない構文なので調べてから回答させていただきます。
LouiS0616

2020/02/11 04:02 編集

# => 0 のことでしょうか? これはただのコメントなので、構文上の意味はありません。標準出力に表示される値をこのように表現することがあるだけです。
esklia

2020/02/11 06:48 編集

「def func(arg): arg = 42」部分です。 def func(arg): arg = 42 ということでしょうか?
LouiS0616

2020/02/11 06:59

はい。 コメントの都合上インデントが入れられないので一行で書きました。 Pythonの文法上も合法です。あまり推奨されませんが。
guest

0

➁入門python3ではそもそも「実引数が仮引数にコピーされる」というような表現がされていましたが、いまいちピンと来ていないので理解があいまいなままです。「実引数が仮引数にコピーされる」とはどういうことでしょうか?

Pythonの説明としては、これは"嘘"でしょう。
まだよくわかってない人になんとなくイメージだけさせるための方便であって、実際にはそのようには動いていません。

p111

(実)引数を渡して関数を呼び出すとき、それらの値は関数内の対応する(仮)引数にコピーされる

とあります。
"コピーされる"の主語を正しく読み取ってください。
コピーされるのは「変数」ではないです。「値」です。

「実引数が仮引数にコピーされる」は誤った認識からでてきた文です。
「値が、実引数から仮引数にコピーされる」の方がPythonの実行モデルには合っています。("コピーされる"が曖昧なままですが方便ですので)

➁入門python3ではそもそも「実引数が仮引数にコピーされる」というような表現がされていましたが、いまいちピンと来ていないので理解があいまいなままです。「実引数が仮引数にコピーされる」とはどういうことでしょうか?

Pythonの説明としては、これは"嘘"でしょう。
まだよくわかってない人になんとなくイメージだけさせるための方便であって、実際にはそのようには動いていません。

p111

(実)引数を渡して関数を呼び出すとき、それらの値は関数内の対応する(仮)引数にコピーされる

とあります。
"コピーされる"の主語を正しく読み取ってください。
コピーされるのは「変数」ではないです。「値」です。

「実引数が仮引数にコピーされる」は誤った認識からでてきた文です。
「値が、実引数から仮引数にコピーされる」の方がPythonの実行モデルには合っています。("コピーされる"が曖昧なままですが方便ですので)


やっぱり「方便としての嘘」なのが気になったので、上の最後の方を取り消し線にしました。

pyhton

1a = [] //★1 2 3b = a //★2

とした時「値がaからbにコピーされている」というようなことは決して起こっていません。
この時、★1の右辺で作られたリストのオプジェクトがあって、★2によってaもbもその同じオブジェクトを指している/参照している/束縛している、という状態になります。
より精確には「右辺のaを評価してでてきた値」をbに束縛する、という動作がなされます。

だから、

python

1b.append(1) //3 2 3print(a) //4

とすると★3で「aとbが指しているリストのオブジェクト」に追加されるので、★4は[1]という結果になります。

python

1b = [] //5 2 3b.append(2) //6 4 5print(a) //7

とすると★5で新しく別のリストオブジェクトが作られて、aは★1で作られたオブジェクト、bは★5で作られたオブジェクトを指している、という状況になります。だから★6でリストに2を追加してもaが指しているオブジェクトには影響がなく、★7の結果は[1]のままです。

さて。
関数呼び出しをした時の、実引数から仮引数への受け渡しの挙動は★2で起こることにそっくりです。

呼び出された側の関数のローカルな名前空間上の名前 = 呼び出した側の引数を評価した値

ということが起こっています。

k('NI!')という式を評価すると、「式'NI!'を評価したオブジェクト」が「kの実行時にできるローカルな名前空間」にsayingという名前で束縛されて、そののちkの本体が実行されます
i(saying)という式を評価すると、「式'sayingを評価したオブジェクト」が「iの実行時にできるローカルな名前空間」にquoteという名前で束縛されて、そののちiの本体が実行されます

投稿2020/02/12 00:01

編集2020/02/13 00:14
quickquip

総合スコア11235

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

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

quickquip

2020/02/12 00:11

クロージャの前で止まっているのだろうと認識したので前にあった回答は削除しました。 回答ごと削除したいところではありますが削除依頼が面倒なシステムですので残しておきます。 すみませんが、悪しからず。
esklia

2020/02/16 14:31

ご回答ありがとうございます。おっしゃる通りクロージャはこれからやるところです。「参照している/束縛している」という概念は抜け落ちていたので大変勉強になりました。
guest

0

仮引数と実引数のところだけ説明してみます。
とは言っても、検索して出てくるようなことしか書きませんが。

まずは「引数」です。関数というのは、うけとった引数やその他の情報を使って、何かをしたり、値を返すものです。

例えば、「2を足す」という関数を考えます。

この関数に5を引数として渡すと7が返ります。 このときの「5」を実際に動作するときの引数なので「実引数」と呼びます。

実引数には変数を使うこともできますが、この時に渡されるのは変数そのものではなくて、その変数が示しているものです。
たとえばi(saying) としたときに関数i に渡されているのはsayingではなくて、そのときのsayingの値であるNI!です。

さて、「2を足す」という関数を定義する場合には、一時的にその引数を入れておく変数を定義します。 この変数を「仮引数」と呼びます。

i関数を定義するときに使っているquoteがこの場合の仮引数です。
i('Hello')と呼ばれたときには、quoteにHelloという文字列が入り、i('Bye')と呼ばれたときには、quoteにByeという文字列が入り、i(greeting)と呼ばれたときには、quoteにその時にgreetengという変数に入っている値が入り、i(saying)と呼ばれたときにはquoteにその時にsayingという変数に入っている値が入るということです

もし、コピーという言葉にひっかかっているのであれば、あまり気にしなくてもいいと思います。たぶんsayingに入っていた値がi関数に渡されるてもsayingに入っていた値はなくならないよということを表したかっただけだと思います。

投稿2020/02/11 14:17

編集2020/02/12 14:38
TakaiY

総合スコア13790

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

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

esklia

2020/02/11 14:29

ご回答くださりありがとうございます。 >>>実引数には変数を使うこともできます。ですが、この時に渡されるのは変数ではなく、その変数が示しているものです。i(saying) で関数i に渡されているのは、sayingではなく、そのときの値であるNI!です。 ここが自分の抜け落ちていて最も理解につながる部分でした。 「i('Hello')と呼びだすと、quoteに'Hello'が代入される」というように解釈して不都合はありませんでしょうか?代入されるという表現は語弊がありますか?
TakaiY

2020/02/12 14:32

一般的には「代入される」で問題ないですよ。
esklia

2020/02/15 10:00

ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問