関数の入力パラメータを変更することは、予期せぬ副作用を引き起こす可能性があります。
本記事では、入力パラメータの変更によって発生する問題と、それを防ぐためのベストプラクティスについて解説します。
入力パラメータの変更がもたらす問題
以下のコードは、ユーザーの残高を更新する関数の例です。
def update_user_balance(user, amount): user['balance'] += amount save_user_to_database(user) user = {'id': 1, 'name': 'Alice', 'balance': 1000} update_user_balance(user, 500) print(user['balance']) # 1500
この関数は、ユーザーの残高を更新し、データベースに保存することを目的としています。
しかし、関数内で入力パラメータ user を直接変更しているため、呼び出し元の user オブジェクトにも影響を与えてしまいます。
これは、呼び出し元にとって予期せぬ動作となる可能性があります。
呼び出し元は、user オブジェクトを関数に渡しても、そのオブジェクトが変更されないことを期待するかもしれません。
入力パラメータの変更による問題は、以下のようなものがあります。
- 呼び出し元のコードに予期せぬ影響を与える
- バグの原因となる
- コードの理解を困難にする
解決策: 新しいオブジェクトを返す
入力パラメータを変更せずに、新しいオブジェクトを返すことで、予期せぬ副作用を防ぐことができます。
def update_user_balance(user, amount): updated_user = user.copy() updated_user['balance'] += amount save_user_to_database(updated_user) return updated_user user = {'id': 1, 'name': 'Alice', 'balance': 1000} updated_user = update_user_balance(user, 500) print(user['balance']) # 1000 print(updated_user['balance']) # 1500
この例では、update_user_balance 関数内で user オブジェクトのコピーを作成し、そのコピーに対して残高の更新を行っています。
そして、更新後のオブジェクトを返しています。
これにより、呼び出し元の user オブジェクトは変更されず、予期せぬ副作用を防ぐことができます。
パフォーマンスへの影響を考慮する
入力パラメータを変更せずに新しいオブジェクトを返すことは、メモリ使用量や実行時間に影響を与える可能性があります。
特に、大量のデータを扱う場合や、パフォーマンスが重要な場合は、入力パラメータを変更せざるを得ないこともあります。
その場合は、関数名やドキュメントで入力パラメータが変更されることを明示し、呼び出し元が適切に対処できるようにします。
def sort_list_in_place(lst): """ リストを in-place でソートする関数 注意: この関数は入力リストを変更します """ lst.sort() numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5] sort_list_in_place(numbers) print(numbers) # [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
この例では、sort_list_in_place 関数は、パフォーマンス上の理由から入力リストを直接変更しています。
ただし、関数名と docstring で入力リストが変更されることを明示的に説明しています。
まとめ
入力パラメータを変更することは、予期せぬ副作用を引き起こす可能性があります。
新しいオブジェクトを返すことで、予期せぬ副作用を防ぐことができます。
ただし、パフォーマンスが重要な場合は、入力パラメータを変更せざるを得ないこともあります。
その場合は、関数名やドキュメントで変更を明示し、呼び出し元が適切に対処できるようにします。
入力パラメータの変更に注意することで、バグを減らし、コードの理解を容易にすることができるでしょう。