Предупреждение о переменном аргументе default в PyCharm

Я использую PyCharm (Python 3) для записи функции Python, которая принимает словарь в качестве аргумента с 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)

В IDE attachment={} окрашено в желтый цвет. Перемещение мыши над ним показывает предупреждение.

Значение аргументов по умолчанию изменено

Эта проверка обнаруживает, когда измененное значение в виде списка или словаря обнаружено в значении по умолчанию для аргумента.

Значения аргументов по умолчанию оцениваются только один раз во время определения функции, что означает, что изменение значения по умолчанию для аргумента будет влиять на все последующие вызовы функции.

Что это значит и как я могу это решить?

Ответ 1

Если вы не изменяете "изменяемый аргумент по умолчанию" или передаете его где-нибудь там, где он может быть изменен, просто игнорируйте это сообщение, потому что ничего не нужно "фиксировать".

В вашем случае вы только распаковываете (который делает неявную копию) "изменяемый аргумент по умолчанию" - так что вы в безопасности.

Если вы хотите "удалить это предупреждающее сообщение", вы можете использовать None как значение по умолчанию и установить его на {} если оно 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)

Просто объясните, что это значит: некоторые типы в Python являются неизменяемыми (int, str ,...), другие являются изменяемыми (например, dict, set, list ,...). Если вы хотите изменить неизменяемые объекты, создается другой объект, но если вы изменяете изменяемые объекты, объект остается тем же, но содержимое изменяется.

Сложная часть состоит в том, что переменные класса и аргументы по умолчанию создаются при загрузке функции (и только один раз), что означает, что любые изменения в "изменяемом аргументе по умолчанию" или "изменяемой переменной класса" являются постоянными:

def func(key, value, a={}):
    a[key] = value
    return a

>>> print(func('a', 10))  # that expected
{'a': 10}
>>> print(func('b', 20))  # that could be unexpected
{'b': 20, 'a': 10}

PyCharm, вероятно, показывает это предупреждение, потому что легко ошибиться случайно (см., Например, "Наименьшее удивление" и аргумент Mutable Default Argument и все связанные вопросы). Тем не менее, если вы сделали это специально (хорошие использует для изменяемых значений аргумента аргумента функции?) Предупреждение может быть раздражающим.

Ответ 2

Вы можете заменить изменяемые аргументы по умолчанию на None. Затем проверьте внутри функции и назначьте значение по умолчанию:

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)

Это работает, потому что None оценивает значение False поэтому мы назначаем пустой словарь.

В общем случае вы можете явно проверить None поскольку другие значения могут также оцениваться как False, например, 0, '', set(), [] и т.д. - все False-y. Если ваше значение по умолчанию не равно 0 и, например, равно 5, то вы не захотите топать на 0 будет передан как действительный параметр:

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

Ответ 3

Чтобы перефразировать предупреждение: каждый вызов этой функции, если он использует значение по умолчанию, будет использовать одно и то же значение. Пока вы никогда не меняете это значение, факт, что он изменен, не имеет значения. Но если вы его измените, последующие вызовы начнутся с измененного значения, которое, вероятно, не то, что вы хотите.

Одним из решений, позволяющим избежать этой проблемы, было бы иметь неизменяемое значение по умолчанию и установить для параметра значение {} если это значение используется по умолчанию:

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)