关于PyCharm

时间:2017-01-16 23:43:57

标签: python pycharm warnings

我正在使用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,表示修改参数的默认值   将影响该函数的所有后续调用。

这是什么意思,我该如何解决?

5 个答案:

答案 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中的某些类型是不可变的(strdict,...)其他类型是可变的(如{{1} },setlist,...)。如果要更改不可变对象,则会创建另一个对象 - 但是如果更改了可变对象,则该对象保持不变,但它的内容会发生变化。

棘手的部分是在加载函数时(并且只加载一次)创建类变量和默认参数,这意味着对"可变默认参数的任何更改"或"可变类变量"是永久的:

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)