Inappropriately marked as a duplicate question. This has nothing to do with not knowing how to pass a variable by reference. It is asking if there is a way to mitigate the issues with keeping a mutable optional argument as a parameter without its value persisting in the function's context on subsequent calls.
So I just had this happen to me today and am now familiar with this trap that many new Python developers fall into: mutable default arguments. For brevity, here's an example of what I'm taking about:
def MyClass(object):
...
def my_func(self,my_str="default",my_dict={},**kwargs):
my_dict.update(kwargs)
self.my_dict = my_dict
----------------------------------------
<< a = MyClass()
<< a.my_func(foo='bar')
<< a.my_dict
>> {'foo':'bar'}
<< b = MyClass()
<< b.my_func(bar='baz')
<< b.my_dict
>> {'foo': 'bar', 'bar': 'baz'}
I understand that this is intentional and understand that a good solution would be to modify my_func
to this instead:
def my_func(self,my_str="default",my_dict=None,**kwargs)
if my_dict is None: my_dict = {}
...
However I'm not satisfied with this. It just rubs me the wrong way. I don't want it to be None by default. I want an empty dict by default. So I thought of doing this:
def my_func(self,my_str="default", my_dict={}, **kwargs)
my_dict.update(kwargs)
self.my_dict = my_dict
my_dict = {}
But of course this won't work, you'll get the same behavior as before. But why? Isn't my_dict
persistent and mutable? Or only when convenient for Python? Is there any other way to deal with this aside from simply excluding my_dict
as an optional arg?
I'm really hung up on this because I find it useful to use a default argument to convey a type to the reader, which boosts code readability IMO. It's good because the reader wouldn't need to dive into the function to determine that my_dict
is supposed to be a dict (inb4 suggesting "just use comments" - idealistic, but not very realistic for my purposes).
This "feature" makes little sense and is so incredibly offensive to me that I'm actually very close to believing that it's really a bug in disguise. No one will ever use this and it's almost a dealbreaker for me.
Thanks in advance.
If I understand you right, you are thinking that by assigning
my_dict = {}
, you are "resetting" the value so that next time you call the function, it will be empty again.That's not how default arguments work. Rather, it works something like this:
func.__defaults__
on a plain function, although there are some additional complications if you are looking at a method object.)In other words, Python does not really store the value of
my_dict
across calls. It stores one default value, and retrieves it on every call (unless you pass a new one).When you do
my_dict = {}
, you are just assigning a new value to the local variablemy_dict
, which leaves the secret object unaffected. When you do something likemy_dict.update()
, on the other hand, you are mutating the secret object. (If it surprises you that assigning to a variable and calling a method likeupdate
have different effects, then you should search for a great many other questions on here about assignment, variable binding, mutable vs immutable objects, and the like in Python.)