よくあるPythonの問題 - 2.可変引数
By JoeVu, at: 2023年1月11日14:20
Estimated Reading Time: __READING_TIME__ minutes


Python 可変引数は、関数呼び出し内で変更できる引数の種類です。これらの引数は参照渡しされ、関数内で変更できます。これにより、コードで予期せぬ副作用が発生し、潜在的なエラーや予期せぬ動作につながる可能性があります。
可変関数引数
Pythonの可変引数は、Pythonでプログラミングする際の一般的な問題であり、コードのデバッグを試みる際に混乱を引き起こす可能性があります。
たとえば、関数がリストを引数として受け取る場合、関数内でそのリストを変更すると、関数外でも変更されます。値が変わることを期待していない場合、デバッグで検出するのが困難になる可能性があります。
def append_to_list(a_list:list, element:int):
if element > 0:
a_list.append(element)
print ("Values inside the function: ", a_list)
a_list = [1, 2, 3]
append_to_list(a_list, 5)
print ("Values outside the function: ", a_list)
出力
Values inside the function: [1, 2, 3, 5]
Values outside the function: [1, 2, 3, 5]
関数append_to_list()は、要素が0より大きい場合に要素を追加することにより、元のリストを変更しました。この動作は常に望ましいとは限りません。予期しない結果と追跡困難なバグにつながる可能性があるためです。
これらの問題を回避する一般的な方法は、変更する前に引数のコピーを作成することです。たとえば、次のコードでは元のリストは変更されません。
def append_to_list(a_list:list, element:int):
if element > 0:
a_list = a_list + [element]
print ("Values inside the function: ", a_list)
a_list = [1, 2, 3]
append_to_list(a_list, 5)
print ("Values outside the function: ", a_list)
出力
Values inside the function: [1, 2, 3, 5]
Values outside the function: [1, 2, 3]
辞書と集合でも同じ問題が発生します。
def update_dictionary(a_dict, keyword, value):
a_dict[keyword] = value
print ("Values inside the function: ", a_dict)
a_dict = {"count": 1}
update_dictionary(a_dict, "message", "good")
print ("Values outside the function: ", a_dict)
出力
Values inside the function: {'count': 1, 'message': 'good'}
Values outside the function: {'count': 1, 'message': 'good'}
解決策
1. 変更する前に可変引数の複製を作成する
例:
import copy
def update_list(a_list):
a_list_copy = copy.copy(a_list)
a_list_copy.append(4)
return a_list_copy
original_list = [1, 2, 3]
modified_list = update_list(original_list)
print(original_list)
print(modified_list)
出力
[1, 2, 3]
[1, 2, 3, 4]
2. 可変引数の代わりに不変引数を使用する。例:Frozen Dictのようなライブラリを使用して、不変の辞書を作成する
from frozendict import frozendict
frozen_dict = frozendict({ 'hello': 'World' })
print(frozen_dict)
frozendict.frozendict({'hello': 'World'})
print(frozen_dict['hello'])
World
frozen_dict["another_key"] = "value"
TypeErrorで例外が発生します
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[44], line 1
----> 1 frozen_dict["another_key"] = "value"
File ~/.pyenv/versions/3.10.8/envs/glinteco-website/lib/python3.10/site-packages/frozendict/core.py:198, in frozendict.__setitem__(self, key, val, *args, **kwargs)
197 def __setitem__(self, key, val, *args, **kwargs):
--> 198 raise TypeError(
199 f"'{self.__class__.__name__}' object doesn't support item "
200 "assignment"
201 )
TypeError: 'frozendict' object doesn't support item assignment
Python可変引数とそのコードの動作への影響を理解することが重要です。予期せぬ副作用を回避するには、代わりに不変引数を使用するのが最適です。不変引数は関数内で変更できず、参照渡しではなく値渡しされます。
これにより、デバッグが容易になり、予期せぬ動作の可能性が低くなります。