Warning about mutable default argument in PyCharm Warning about mutable default argument in PyCharm python python

Warning about mutable default argument in PyCharm


If you don't alter the "mutable default argument" or pass it anywhere where it could be altered just ignore the message, because there is nothing to be "fixed".

In your case you only unpack (which does an implicit copy) the "mutable default argument" - so you're safe.

If you want to "remove that warning message" you could use None as default and set it to {} when it's None:

def put_wall_post(self,message,attachment=None,profile_id="me"):    if attachment is None:        attachment = {}    return self.put_object(profile_id,"feed",message = message,**attachment)

Just to explain the "what it means": Some types in Python are immutable (int, str, ...) others are mutable (like dict, set, list, ...). If you want to change immutable objects another object is created - but if you change mutable objects the object remains the same but it's contents are changed.

The tricky part is that class variables and default arguments are created when the function is loaded (and only once), that means that any changes to a "mutable default argument" or "mutable class variable" are permanent:

def func(key, value, a={}):    a[key] = value    return a>>> print(func('a', 10))  # that's expected{'a': 10}>>> print(func('b', 20))  # that could be unexpected{'b': 20, 'a': 10}

PyCharm probably shows this Warning because it's easy to get it wrong by accident (see for example “Least Astonishment” and the Mutable Default Argument and all linked questions). However, if you did it on purpose (Good uses for mutable function argument default values?) the Warning could be annoying.


You can replace mutable default arguments with None. Then check inside the function and assign the default:

def put_wall_post(self, message, attachment=None, profile_id="me"):    attachment = attachment if attachment else {}    return self.put_object(profile_id, "feed", message=message, **attachment)

This works because None evaluates to False so we then assign an empty dictionary.

In general you may want to explicitly check for None as other values could also evaluate to False, e.g. 0, '', set(), [], etc, are all False-y. If your default isn't 0 and is 5 for example, then you wouldn't want to stomp on 0 being passed as a valid parameter:

def function(param=None):    param = 5 if param is None else param


This is a warning from the interpreter that because your default argument is mutable, you might end up changing the default if you modify it in-place, which could lead to unexpected results in some cases. The default argument is really just a reference to the object you indicate, so much like when you alias a list to two different identifiers, e.g.

>>> a={}>>> b=a>>> b['foo']='bar'>>> a{'foo': 'bar'}

if the object is changed through any reference, whether during that call to the function, a separate call, or even outside the function, it will affect future calls the function. If you're not expecting the behavior of the function to change at runtime, this could be a cause for bugs. Every time the function is called, it's the same name being bound to the same object. (in fact, I'm not sure if it even goes through the whole name binding process each time? I think it just gets another reference.)

the (likely unwanted) behavior

You can see the effect of this by declaring the following and calling it a few times:

>>> def mutable_default_arg (something = {'foo':1}):    something['foo'] += 1    print (something)    >>> mutable_default_arg(){'foo': 2}>>> mutable_default_arg(){'foo': 3}

Wait, what? yes, because the object referenced by the argument doesn't change between calls, changing one of its elements changes the default. If you use an immutable type, you don't have to worry about this because it shouldn't be possible, under standard circumstances, to change an immutable's data. I don't know if this holds for user-defined classes, but that is why this is usually just addressed with "None" (that, and you only need it as a placeholder, nothing more. Why spend the extra RAM on something more complicated?)

duct-taped problems...

In your case, you were saved by an implicit copy, as another answer pointed out, but it's never a good idea to rely on implicit behavior, especially unexpected implicit behavior, since it could change. That's why we say "explicit is better than implicit". Besides which, implicit behavior tends to hide what's going on, which could lead you or another programmer to removing the duct tape.

...with simple (permanent) solutions

You can avoid this bug magnet completely and satisfy the warning by, as others have suggested, using an immutable type such as None, checking for it at the start of the function, and if found, immediately replacing it before your function gets going:

def put_wall_post(self, message, attachment=None, profile_id="me"):    if attachment == None:        attachment = {}     return self.put_object(profile_id, "feed", message=message, **attachment)

Since immutable types force you to replace them (Technically, you are binding a new object to the same name. in the above, the reference to None is overwritten when attachment is rebound to the NEW empty dictionary) instead of updating them, you know attachment will always start as None unless specified in the call parameters, thus avoiding the risk of unexpected changes to the default.

(As an aside, when in doubt whether an object is the same as another object, compare them with is or check id(object). The former can check whether two references refer to the same object, and the latter can be useful for debugging by printing a unique identifier —typically the memory location— for the object)