5

我有一些具有属性字典的对象,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应该只是一个空字典!{}

这让我想知道这是否不是同时使用 **kwargs 类似的参数的问题attrs={}

有任何想法吗?

4

5 回答 5

11

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

问题是,如果只有一个attrs存在,那么对于您所说的每个 Thing 实例,您的每个实例self.attrs = attrsself.attrs成员变量都指向attrs.

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

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,您将完全避免您的问题。

于 2011-07-22T18:15:37.450 回答
2

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

class Thing:
    def __init__(self, attrs=None, **kwargs):
        self.attrs = attrs or {}
        self.attrs.update(kwargs)
于 2011-07-22T18:20:23.417 回答
1

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

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

有关此问题的精彩讨论,请参阅Python 中的“Least Astonishment”: stackoverflow 上的可变默认参数。另请参阅effbot上 Python 中的默认参数值

于 2011-07-22T18:16:40.483 回答
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]
于 2011-07-22T18:18:27.503 回答
1

仅仅为了它的价值——我们可以attrs通过简单地不改变它来避免“是一个共享的可变对象”问题。与其将kwargsintoattrs转储,不如将它们都转储到新的字典中。那么 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 作为默认参数并检查它”的成语,我个人觉得这很不雅。sgusc 的想法是正确的:考虑到 Python 的**kwargs. :)

于 2011-07-22T19:22:23.460 回答