Time ago I wrote about the perils of optional parameters in Python. Now I’ve found another reason why they are dangerous, although we still need them.
What we have realized today is that, default values for optional arguments might share its state among multiple calls. You might have a Borg pattern where you don’t want it, and that is dangerous:
def my_func_with_optional_list(arg1, arg2=[]):
arg2.append(arg1)
return arg2
def my_func_with_optional_dict(arg1, arg2={}):
arg2[arg1] = arg1
return arg2
if __name__ == '__main__':
print my_func_with_optional_list(1)
print my_func_with_optional_list(1)
print my_func_with_optional_dict('carlos')
print my_func_with_optional_dict('oscar')
Can you see anything that could keep the state between calls? I can’t, however, this is the output:
[1]
[1, 1]
{'carlos': 'carlos'}
{'oscar': 'oscar', 'carlos': 'carlos'}
What happen? My understanding is that, because the list and dictionary objects are mutable and they are defined along with the function itself, python creates them in the global namespace.
We are now reviewing again every function we’ve written searching for this pattern.