Python函数“记住”早期的参数(** kwargs)

时间:2011-07-22 18:12:23

标签: python kwargs

我有一些具有属性字典的对象obj.attrs。为方便起见,这些对象的构造函数接受dict和/或**kwargs

看起来有点像这样:

class Thing:
    def __init__(self, attrs={}, **kwargs):
        for arg in kwargs:
            attrs[arg] = kwargs[arg]
        self.attrs = attrs

Thing({'color':'red'})Thing(color='red')的作用相同。

我的问题是构造函数以某种方式记住传递给它的最后一个attrs值。

例如:

>>> thing1 = Thing(color='red')
>>> thing2 = Thing()
>>> thing2.attrs
{'color': 'red'}

...但是thing2.attrs应该只是一个空的字典! {}

这让我想知道使用 {/ em> **kwargs 这样的参数(例如attrs={})是否存在问题。

有什么想法吗?

5 个答案:

答案 0 :(得分:11)

使用默认参数的问题是它们实际上只存在一个实例。当您在 init 方法定义中说attrs={}时,该单个默认{}实例是每次调用该方法的默认值(它不会每次都创建一个新的默认空dict,它使用相同的一个)。

问题在于,如果只有一个attrs存在,然后对于每个Thing实例,你说self.attrs = attrs,那么你的每一个实例的self.attrs成员变量都指向到attrs的单个共享默认实例。

另一个问题是,这不完全是多余的吗?您可以使用**kwargs传递关键字/值args或字典。如果您刚刚定义了这个:

class Thing:
    def __init__(self, **kwargs):
        for arg in kwargs:
            self.attrs[arg] = kwargs[arg]

所有这些策略仍然有效:

thing1 = Thing(color='red')

thing2 = Thing(**{'color':'red'})

my_dict = {'color' : 'red'}
thing3 = Thing(**my_dict)

因此,如果您只是以这种方式定义和使用Thing,那么您将完全避免您的问题。

答案 1 :(得分:2)

如何更改签名以便每次都创建字典

class Thing:
    def __init__(self, attrs=None, **kwargs):
        self.attrs = attrs or {}
        self.attrs.update(kwargs)

答案 2 :(得分:1)

attrs是字典的引用。创建新对象时,self.attrs指向该字典。当您从kwargs分配值时,它会转到此词典。

现在,当您创建第二个实例时,它的self.attrs也指向同一个字典。因此,它获取该字典中的任何数据。

有关此问题的详细讨论,请参阅stackoverflow上的"Least Astonishment" in Python: The Mutable Default Argument。另请参阅Default Parameter Values上的Python中的effbot

答案 3 :(得分:1)

您想将代码更改为:

class Thing:
    def __init__(self, attrs=None, **kwargs):
        attrs = {} if attrs is None else attrs
        for arg in kwargs:
            attrs[arg] = kwargs[arg]
        self.attrs = attrs

正如其他人所指出的那样,默认参数的值在定义时被评估一次,而不是每次调用函数时。通过使用可变容器,所有后续调用都可以看到对容器的每次添加,因为每次调用都使用与默认值相同的容器。

可能你只是使用attrs作为提供初始值的方式,而你根本就不想分享dicts。在这种情况下,请使用:

class Thing:
    def __init__(self, attrs=None, **kwargs):
        self.attrs = {}
        if attrs:
            self.attrs.update(attrs)
        for arg in kwargs:
            self.attrs[arg] = kwargs[arg]

答案 4 :(得分:1)

只是为了它的价值 - 我们可以通过简单地不改变它来避免“attrs是一个共享的可变对象”问题。而不是将kwargs转储到attrs中,而是将它们转储到新的字典中。然后default-argument-object将始终为{}

class Thing:
    def __init__(self, attrs = {}, **kwargs):
        self.attrs = {}
        # Don't write the loop yourself!
        self.attrs.update(attrs)
        self.attrs.update(kwargs)

我只是提到这一点,因为每个人都急于描述“使用None作为默认参数并检查它”这个成语,我个人认为这是相当hackish。 sgusc有正确的想法:考虑到Python **kwargs的一般特性,整个努力都没有用。 :)