[英]Python function “remembering” earlier argument (**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
应该只是一个空字典! {}
这让我想知道使用**kwargs
和类似attrs={}
的参数是否不是问题。
有任何想法吗?
使用默认 arguments 的问题在于,它们实际上只存在一个实例。 当您在init方法定义中说attrs={}
时,该单个默认 {} 实例是对该方法的每次调用的默认值(它不会每次都创建一个新的默认空字典,它使用相同的)。
问题是,如果只有一个attrs
存在,然后对于您所说的每个 Thing 实例self.attrs = attrs
,则每个实例的self.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,您将完全避免您的问题。
如何更改签名以便每次都创建字典
class Thing:
def __init__(self, attrs=None, **kwargs):
self.attrs = attrs or {}
self.attrs.update(kwargs)
attrs
是对字典的引用。 当您创建一个新的 object 时, self.attrs
指向该字典。 当您从kwargs
分配一个值时,它会进入这个字典。
现在,当您创建第二个实例时,它的self.attrs
也指向同一个字典。 因此,它获取该字典中的任何数据。
有关此问题的精彩讨论,请参阅Python 中的“Least Astonishment”:stackoverflow 上的可变默认参数。 另请参阅 effbot 上Python中的默认参数值。
您想将代码更改为:
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
正如其他人指出的那样,默认参数的值在定义时被评估一次,而不是每次调用 function 时。 通过使用可变容器,所有后续调用都可以看到对容器的每个添加,因为每个调用都使用相同的容器作为默认值。
可能您只使用 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]
仅仅为了它的价值——我们可以通过简单地不改变它来避免“ attrs
是一个共享的可变对象”问题。 不要将kwargs
转储到attrs
中,而是将它们都转储到新的 dict 中。 那么 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
的普遍性,整个努力没有用处。 :)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.