Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Warning about mutable default argument in PyCharm

I am using PyCharm (Python 3) to write a Python function which accepts a dictionary as an argument with attachment={}.

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

In the IDE, attachment={} is colored yellow. Moving the mouse over it shows a warning.

Default arguments value is mutable

This inspection detects when a mutable value as list or dictionary is detected in a default value for an argument.

Default argument values are evaluated only once at function definition time, which means that modifying the default value of the argument will affect all subsequent calls of the function.

What does this mean and how can I resolve it?

like image 294
bin Avatar asked Jan 16 '17 23:01

bin


People also ask

What is a mutable default argument in Python?

Python's default arguments are evaluated once when the function is defined, not each time the function is called (like it is in say, Ruby). This means that if you use a mutable default argument and mutate it, you will and have mutated that object for all future calls to the function as well.

Why shouldn't you make the default arguments an empty list?

Answer: d) is a bad idea because the default [] will accumulate data and the default [] will change with subsequent calls.

What do you mean by default argument?

In computer programming, a default argument is an argument to a function that a programmer is not required to specify. In most programming languages, functions may take one or more arguments. Usually, each argument must be specified in full (this is the case in the C programming language).

What is default argument in PHP precedence?

The default arguments must be constant expressions. They cannot be variables or function calls. PHP allows you to use a scalar value, an array, and null as the default arguments.


2 Answers

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.

like image 168
MSeifert Avatar answered Sep 16 '22 15:09

MSeifert


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 
like image 21
Peter Wood Avatar answered Sep 18 '22 15:09

Peter Wood