🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Python 3.x

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

Q&A

3回答

261閲覧

定義した関数について

taromtia

総合スコア18

Python 3.x

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

0グッド

0クリップ

投稿2021/02/13 13:15

前提・実現したいこと

リストに関数を入れて計算した値の合計を出す
1つ目のコードの出力は6なのに対して、二つ目の出力結果は3と12でした。
なぜ二つ目のprint(abc(1))の出力は6にならないのでしょうか?

おそらく定義した関数を複数回使うと何かが上書きされるのだと思いますが、何が悪いのかわかりません。

該当のソースコード

Python3

1list=[] 2for i in range(3): 3 list.append(lambda x:x+i-1) 4 5sum=0 6 7for a in list: 8 sum += a(1) 9print(sum)

Python3

1def abc(price,list=[]): 2 3 for i in range(3): 4 list.append(lambda x:x+i-1) 5 6 sum=0 7 8 for a in list: 9 sum += a(price) 10 11 return sum 12 13print(abc(0)) 14print(abc(1))

### 試したこと
一つ目のコードをかいた

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

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

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

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

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

guest

回答3

0

引き数のデフォルト値[]は、関数の定義の時にオブジェクトが作られ、そのオブジェクトが毎回の関数呼び出しで使われます。
従って、オブジェクトappendすると次回の呼び出しでは、appendされた結果のオブジェクトが初期値になります。

1つめのコードのように引き数でないローカル変数にしましょう。

また、listという名前は標準のリスト型の名前なので、別の名前にしましょう。

投稿2021/02/13 13:46

otn

総合スコア85893

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

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

0

公式ドキュメント 8.6. 関数定義に以下のように書かれています。

デフォルト引数の式は関数が定義されるときにただ一度だけ評価され、同じ "計算済みの" 値が呼び出しのたびに使用されることを意味します。この仕様を理解しておくことは特に、デフォルト引数値がリストや辞書のようなミュータブルなオブジェクトであるときに重要です: 関数がこのオブジェクトを変更 (例えばリストに要素を追加) すると、このデフォルト値が変更の影響を受けてしまします。

これがわかれば理解できます。

投稿2021/02/13 13:35

ppaul

総合スコア24670

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

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

taromtia

2021/02/14 10:15

この情報で何とか解決できました。ありがとうございます。
guest

0

複合的な、しかもそれぞれが分かりづらい理由で挙動を追いづらくなっています。

  1. ループ中で定義した関数には注意

次のように、イメージと異なる結果になります。

Python

1funcs = [] 2for i in range(3): 3 funcs.append(lambda: print(f'i = {i}')) 4 5for func in funcs: 6 func()

実行結果 Wandbox

i = 2 i = 2 i = 2

次のように回避する方法が知られています。

Python

1for i in range(3): 2 funcs.append(lambda e=i: print(f'e = {e}')) # 外部変数iを直接使わない

参考: ループの中で異なる値で定義されたラムダ式が、同じ値を返すのはなぜですか?

  1. リストをデフォルト引数にしない

Python独特の挙動で、嵌りやすいです。

Python

1def func(e, lst=[]): 2 lst.append(e) 3 print(lst) 4 5func(0) 6func(1) 7func(2)

実行結果 Wandbox

[0] [0, 1] [0, 1, 2]

次のように回避する方法が知られています。

Python

1def func(e, lst=None): # デフォルト引数をNoneにする 2 if lst is None: 3 lst = [] 4 5 lst.append(e) 6 print(lst)

参考: なぜオブジェクト間でデフォルト値が共有されるのですか?

トレース

一つ目のコード

Python

1list=[] 2for i in range(3): 3 list.append(lambda x:x+i-1) 4 # ループ終了時点で全要素が lambda x: x+2-1 になっている 5 6sum=0 7 8for a in list: # リスト長は3 9 sum += a(1) # a(1) は常に 1+2-1 = 2 を返す 10print(sum) # 3回足したら 3x2 = 6

二つ目のコード・前半

Python

1def abc(price,list=[]): 2 3 for i in range(3): 4 list.append(lambda x:x+i-1) 5                  # 全要素が lambda x: x+2-1 6 7 sum=0 8 9 for a in list: 10 sum += a(price) # a(0) は常に 0+2-1 = 1 を返す 11 12 return sum # 3回足したら 3 13 14print(abc(0))

二つ目のコード・後半

Python

1def abc(price,list=[]): # 関数突入時点で list = [lambda x: x+2-1 が3つ] 2 3 for i in range(3): 4 list.append(lambda x:x+i-1) 5   # 全要素が lambda x: x+2-1 6 7 sum=0 8 9 for a in list: # リスト長は6 10 sum += a(price) # a(1) は常に 1+2-1 = 2 を返す 11 12 return sum # 6回足したら 12 13 14print(abc(1))

解決案

少なくとも今回のコードについては、あまり難しく考える必要は無いでしょう。

Python

1def abc(price): 2 ret = 0 3 for i in range(3): 4 ret += price + i - 1 5 6 return ret

あるいは

Python

1def abc(price): 2 return sum( 3 price + i - 1 for i in range(3) 4 )

更には

Python

1def abc(price): 2 n = 3 3 return n*(price-1) + (n*(n-1)//2)

投稿2021/02/13 13:31

編集2021/02/13 14:04
LouiS0616

総合スコア35668

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問