【Python 雑談・雑学】 Common Mistake: mutable なオブジェクトを関数のデフォルト値に使っちゃダメでしょ、の話 投稿一覧へ戻る

Tags: Python , miscellaneous , mutable , immutable , function , default

Published 2020年5月27日13:15 by T.Tsuyoshi

悩んでいたので「どうしたの?」と声をかけたら ...


さて問題です。
print(a2) では何が出力されるでしょうか?


def enroll_class(name: str, attendee: str, attendees: list = []):
attendees.append(attendee)

return {
'name': name,
'attendee': attendee,
'attendees': attendees
}

a1 = enroll_class('math', 'Nana')
a2 = enroll_class('programming', 'Saki')

print(a2)



正解は


# {'name': 'programming', 'attendee': 'Saki', 'attendees': ['Nana', 'Saki']}




attendees リストの 'Nana' は何処から出てきたのでしょうか?


a2 = enroll_class('programming', 'Saki')
の何処にも 'Nana' は登場しないのに...


これは mutable なオブジェクトをデフォルト値として設定してしまったことから発生しています。


試しに、enroll_class() が呼び出されたときのそれぞれの attendees リストのアドレスを確認します。


def enroll_class(name: str, attendee: str, attendees: list = []):
print(id(attendees))
attendees.append(attendee)

return {
'name': name,
'attendee': attendee,
'attendees': attendees
}

a1 = enroll_class('math', 'Nana')
a2 = enroll_class('programming', 'Saki')

# 106499592
# 106499592




a1、a2 の呼び出しでまったく同じリストを指しています。


これは関数に設定したデフォルト値が、呼び出されたときではなく定義されたときに評価されるからです。


しかも、List は mutable なオブジェクトですから、a2 の呼び出し時における attendees.append() では a1 の呼び出し時のリストに値が追加されて返された結果、だったんです。


この問題への対処方法は2つあります。


1つは mutable なオブジェクトをデフォルト値としてセットするのを止めて (というか、mutable なオブジェクトをデフォルト値としてセットしてはいけません!)、常に呼び出し元から引数として渡すことです。


def enroll_class(name: str, attendee: str, attendees: list):
attendees.append(attendee)

return {
'name': name,
'attendee': attendee,
'attendees': attendees
}

a1 = enroll_class('math', 'Nana', [])
a2 = enroll_class('programming', 'Saki', [])

print(a2) # {'name': 'programming', 'attendee': 'Saki', 'attendees': ['Saki']}



もう1つは、デフォルト値としては None をセットし、関数内でチェックし対処する方法です。


def enroll_class(name: str, attendee: str, attendees: list = None):
if not attendees:
attendees = []
attendees.append(attendee)

return {
'name': name,
'attendee': attendee,
'attendees': attendees
}

a1 = enroll_class('math', 'Nana')
a2 = enroll_class('programming', 'Saki')

print(a2) # {'name': 'programming', 'attendee': 'Saki', 'attendees': ['Saki']}



自分が利用しているオブジェクトが immutable なのか mutable なのか、理解・把握しておくことはかなり重要です。

この投稿をメールでシェアする

0 comments

コメントはまだありません。

コメントを追加する(不適切と思われるコメントは削除する場合があります)