我正在使用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={}
显示为黄色。将鼠标移到它上面会显示警告。
默认参数值是可变的
此检查检测何时可变值为列表或字典 检测到参数的默认值。
默认参数值仅在函数定义时计算一次 time,表示修改参数的默认值 将影响该函数的所有后续调用。
这是什么意思,我该如何解决?
答案 0 :(得分:29)
如果你没有改变"可变的默认参数"或者将它传递到可以改变的任何地方,只是忽略了这个消息,因为没有什么可以修复"。
在你的情况下,仅解包(隐式副本)"可变默认参数" - 这样你就安全了。
如果你想"删除该警告信息"您可以使用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)
:
int
只是解释"它意味着什么":Python中的某些类型是不可变的(str
,dict
,...)其他类型是可变的(如{{1} },set
,list
,...)。如果要更改不可变对象,则会创建另一个对象 - 但是如果更改了可变对象,则该对象保持不变,但它的内容会发生变化。
棘手的部分是在加载函数时(并且只加载一次)创建类变量和默认参数,这意味着对"可变默认参数的任何更改"或"可变类变量"是永久的:
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可能会显示此警告,因为它很容易被误导(例如“Least Astonishment” and the Mutable Default Argument和所有相关问题)。但是,如果您故意这样做(Good uses for mutable function argument default values?),警告可能很烦人。
答案 1 :(得分:3)
您可以使用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
答案 2 :(得分:1)
这是解释器的警告,因为默认参数是可变的,所以如果就地修改默认值,最终可能会更改默认值,这在某些情况下可能导致意外结果。默认参数实际上只是对您指示的对象的引用,就像将列表别名为两个不同的标识符(例如
)一样>>> a={}
>>> b=a
>>> b['foo']='bar'
>>> a
{'foo': 'bar'}
如果通过任何引用更改了对象,则无论是在对该函数的调用期间,在单独的调用中,甚至在函数外部,都将影响以后对该函数的调用。如果您不希望函数的行为在运行时发生更改,则可能是导致错误的原因。每次调用该函数时,都将相同的名称绑定到相同的对象。 您可以通过定义以下内容并调用几次来演示这种行为:
>>> def mutable_default_arg (something = {'foo':1}):
something['foo'] += 1
print (something)
>>> mutable_default_arg()
{'foo': 2}
>>> mutable_default_arg()
{'foo': 3}
等等,什么?是的,因为参数所引用的对象在两次调用之间不会更改,所以更改其元素之一会更改默认值。如果使用不可变类型,则不必担心这一点,因为在标准情况下,不可能更改不可变数据。我不知道这是否适用于用户定义的类,但这就是为什么通常只用“ None”来解决(那,而您只需要将其用作占位符就可以了。为什么要在更多的东西上花费额外的RAM复杂吗?)
对于您而言,您显示给我们的代码似乎很好,因为它不会更改对象本身。但是,尽管在打开附件并将其重新打包为数据时,对put_object的调用会执行隐式副本,但请记住Zen of Python:“显式优于隐式”。通过不明确地解决警告,您只是在寻求导致错误的过程中某处的其他更改,而不作任何假设
如其他人所建议的那样,可以使用None
之类的不可变类型,在函数开始时对其进行检查,如果发现,请立即将其替换,从而完全避免引起该错误的注意并满足警告。在功能开始之前:
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)
由于不可变类型迫使您替换它们(从技术上讲,您是将新对象绑定到相同的名称。在上文中,当附件重新绑定到新的空字典时,不引用任何对象)而不是更新它们,因此您知道{除非在调用参数中指定,否则{1}}始终以attachment
开头,从而避免了意外更改默认值的风险。
(顺便说一句,如果不确定某个对象是否与另一个对象相同,请将其与None
进行比较或检查is
。前者可以检查两个引用是否引用了同一对象,通过打印对象的唯一标识符(通常是内存位置),后者对于调试很有用。
答案 3 :(得分:-1)
列表是可变的,并且在编译时使用def at声明默认值会在某个地址将可变列表分配给变量
using System.Linq;
...
List<Product> source = ...
var bests = source
.Aggregate(new List<Product>(), (s, a) => {
if (s.Count <= 0 || s[0].Price == a.Price)
s.Add(a);
else if (a.Price <= s[0].Price) {
s.Clear();
s.Add(a);
}
return s;
});
Product best = bests.Count == 1 ? bests[1] : default(Product);
要纠正此问题:
def abc(a=[]):
a.append(2)
print(a)
abc() #prints [2]
abc() #prints [2, 2] as mutable thus changed the same assigned list at func delaration points to same address and append at the end
abc([4]) #prints [4, 2] because new list is passed at a new address
abc() #prints [2, 2, 2] took same assigned list and append at the end
答案 4 :(得分:-2)
要重新警告警告:对此函数的每次调用(如果使用默认值)将使用相同的值。只要你永远不改变这个价值,它是可变的这一事实并不重要。但是如果您做更改它,那么后续调用将以修改的值开始,这可能不是您想要的。
避免此问题的一个解决方案是将默认值设置为不可变值,如果使用默认值,则将参数设置为{}
:
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)