検索ガイド -Search Guide-

単語と単語を空白で区切ることで AND 検索になります。
例: python デコレータ ('python' と 'デコレータ' 両方を含む記事を検索します)
単語の前に '-' を付けることで NOT 検索になります。
例: python -デコレータ ('python' は含むが 'デコレータ' は含まない記事を検索します)
" (ダブルクオート) で語句を囲むことで 完全一致検索になります。
例: "python data" 実装 ('python data' と '実装' 両方を含む記事を検索します。'python data 実装' の検索とは異なります。)
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
>>

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

Published 2020年5月27日13:15 by mootaro23

SUPPORT UKRAINE

- Your indifference to the act of cruelty can thrive rogue nations like Russia -

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


さて問題です。
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 なのか、理解・把握しておくことはかなり重要です。