5
Help us understand the problem. What are the problem?

posted at

Python3エンジニア認定基礎試験の勉強でつまずいたところ【ユーザー定義関数編】

はじめに

2022年4月15日、Python3エンジニア認定基礎試験に合格しました。
試験対策は各認定スクールさんが出している模擬試験で行っていました。
模擬試験を解いていて、プログラミング初学者の私がつまずいたところを備忘録としてまとめようと思います。
今後同じ箇所でつまずいた方の一助となれば幸いです。
*問題は一部改変しています。

模擬試験の問題

問1.以下のプログラムを実行した際の出力結果を答えよ。
test.py
def dive_into_code(teacher, L = []):
	L.append(teacher)
    return L

print(dive_into_code('Noro'))
print(dive_into_code('Nakao'))
print(dive_into_code('Miyashita'))

正解がこちら。

console
['Noro']
['Noro', 'Nakao']
['Noro', 'Nakao', 'Miyashita']

ここで私は思いました。
関数dive_into_codeを呼び出す度に、L = []とデフォルト引数が代入されリストが初期化されるので、

脳内cosole
['Noro']
['Nakao']
['Miyashita']

となるのではないか、と。これは間違いでした。

正しくは、Pythonではデフォルト引数の値が評価(代入)されるのは関数を定義した時だけ、です。
関数を呼び出す時にその都度デフォルト引数が評価されるわけではないのでご注意ください。1
ですから、関数の定義時にのみ初期化されたリストL第一引数のteacherappendされて正解の出力結果となります。

デフォルト引数の値が関数定義時のみ評価されることを可視化するため、試しに次のようなプログラムを書いてみました。

test.py
def hoge():
    print('hogehoge')

def dive_into_code(teacher, L = [], H = hoge()):
    L.append(teacher)
    return L

print('---ここから関数呼び出し---')
print(dive_into_code('Noro'))
print(dive_into_code('Nakao'))
print(dive_into_code('Miyashita'))

実行結果がこちら。

console
hogehoge
---ここから関数呼び出し---
['Noro']
['Noro', 'Nakao']
['Noro', 'Nakao', 'Miyashita']

この結果からも、関数dive_into_codeの定義時にのみD = hoge()が評価され、関数の呼び出し時には評価されていないことがわかります。

では、続いての問題も見てみましょう。

問2.以下のプログラムを実行した際の出力結果を答えよ。
test.py
i = 5
i = 6

def f(arg = i):
    i = 7
    print(arg)

i = 8
i = 9
f()

正解がこちら。

console
6

この問題と初めましての時はf()となっていることをわかっていながら9と答えてしまいました。
なぜ6なのか。もうお分かりだと思いますが、先ほど同様、Pythonではデフォルト引数の値が評価(代入)されるのは関数を定義した時だけだからです。
つまり、プログラムが上から実行されて関数fが定義された時、直前で変数iには6を代入していました。このiがデフォルト値としてargに代入されたので、関数fに実引数を渡さなければ評価はarg = 6のまま、というわけです。

では少し書き換えて、次の場合ではどうなるでしょうか。

test.py
i = 5
i = 6

def f(arg = i):
    i = 7
    print(arg)

i = 8
i = 9

f()
f(i)
f()

実行結果は以下となります。

console
6    #f()
9    #f(i)
6    #f()

f(i)と、実引数iを渡した時は直前でiに代入した9となっていますが、もう一度実引数なしで呼び出したf()ではデフォルト値である6になっています。
当然と言われればそれまでですが、関数定義時にされたデフォルト引数の値の評価(今回はarg = 6)は実引数を渡して関数を呼び出しても変わらないことがわかりました。

では最後に、問1を書き換えた以下のプログラムを実行した結果を予想してみてください。

test.py
def dive_into_code(teacher, L = []):
    L.append(teacher)
    return L

M = []
print(dive_into_code('Noro'))
print(dive_into_code('Nakao', M ))
print(dive_into_code('Miyashita'))

実行結果がこちら。

console
['Noro']
['Nakao']
['Noro', 'Miyashita']

関数定義時にされたデフォルト引数の値の評価は実引数を渡して関数を呼び出しても変わらないので、実引数として空のリストMを渡したあとも、関数を定義した時にできたリストがデフォルトの引数となっています。

まとめ

・Pythonではデフォルト引数の値が評価(代入)されるのは関数を定義した時だけ。
・上記から、関数を定義した時に評価されたデフォルト引数の値は、関数に実引数を渡しても変わらない。

最後に

資格としては強いものではありませんが、勉強する中で疑問が疑問を呼び色々と手を動かしながら学ぶうちに新たな知見が得られ、今回の受験は非常に有意義でした。
次はデータ分析の方の受験も考えています。
なにか間違いなどあればコメントでご指摘いただけると、この記事を見られる方、私の勉強になりますので、よろしくお願いします。
初投稿、初学者故の拙さもあったかと思いますが、最後までお読みいただきありがとうございました。

  1. JavaScript、Rubyなど、関数を呼び出した時にデフォルト引数の値が評価される言語もあるようです。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
5
Help us understand the problem. What are the problem?