在Python中使用**kwargs的正确方法

当涉及到默认值时,在Python中使用**kwargs的正确方法是什么?

kwargs返回一个字典,但是设置默认值的最佳方法是什么,或者是否存在默认值?我应该把它作为字典来访问吗?使用get函数?

class ExampleClass:
def __init__(self, **kwargs):
self.val = kwargs['val']
self.val2 = kwargs.get('val2')

一个简单的问题,但我找不到好的资料。在我所见过的代码中,人们用不同的方式来实现它,很难知道该用什么。

553376 次浏览

对于不在字典中的键,可以将默认值传递给get():

self.val2 = kwargs.get('val2',"default value")

但是,如果您计划使用具有特定默认值的特定参数,为什么不首先使用命名参数呢?

def __init__(self, val2="default value", **kwargs):

你会做

self.attribute = kwargs.pop('name', default_value)

self.attribute = kwargs.get('name', default_value)

如果您使用pop,那么您可以检查是否有任何虚假的值发送,并采取适当的操作(如果有)。

你可以这样做

class ExampleClass:
def __init__(self, **kwargs):
arguments = {'val':1, 'val2':2}
arguments.update(kwargs)
self.val = arguments['val']
self.val2 = arguments['val2']

使用**kwargs和默认值很容易。然而,有时候你一开始就不应该使用**kwarg。

在这种情况下,我们并没有充分利用**狼。

class ExampleClass( object ):
def __init__(self, **kwargs):
self.val = kwargs.get('val',"default1")
self.val2 = kwargs.get('val2',"default2")

上面是一个“为什么要麻烦?”的声明。这和

class ExampleClass( object ):
def __init__(self, val="default1", val2="default2"):
self.val = val
self.val2 = val2

当您使用**kwargs时,意味着关键字不仅是可选的,而且是有条件的。有比简单的默认值更复杂的规则。

当您使用**kwargs时,您通常指的是类似于下面的内容,其中不应用简单的默认值。

class ExampleClass( object ):
def __init__(self, **kwargs):
self.val = "default1"
self.val2 = "default2"
if "val" in kwargs:
self.val = kwargs["val"]
self.val2 = 2*self.val
elif "val2" in kwargs:
self.val2 = kwargs["val2"]
self.val = self.val2 / 2
else:
raise TypeError( "must provide val= or val2= parameter values" )

我建议这样做

def testFunc( **kwargs ):
options = {
'option1' : 'default_value1',
'option2' : 'default_value2',
'option3' : 'default_value3', }


options.update(kwargs)
print options


testFunc( option1='new_value1', option3='new_value3' )
# {'option2': 'default_value2', 'option3': 'new_value3', 'option1': 'new_value1'}


testFunc( option2='new_value2' )
# {'option1': 'default_value1', 'option3': 'default_value3', 'option2': 'new_value2'}

然后你可以任意使用这些值

dictionaryA.update(dictionaryB)dictionaryB的内容添加到dictionaryA,覆盖任何重复的键。

虽然大多数答案都是这样说的,例如,

def f(**kwargs):
foo = kwargs.pop('foo')
bar = kwargs.pop('bar')
...etc...

是"the same as"

def f(foo=None, bar=None, **kwargs):
...etc...

这是不对的。在后一种情况下,f可以被调用为f(23, 42),而前一种情况接受命名参数只有——没有位置调用。通常你希望给打电话的人最大的灵活性,因此第二种形式,正如大多数回答所断言的那样,是更可取的:但情况并非总是如此。当您接受许多可选参数,而通常只有少数参数被传递时,强制使用命名参数可能是一个很好的主意(避免在您的调用站点发生意外和不可读的代码!)——threading.Thread就是一个例子。第一种形式是如何在Python 2中实现它。

这个习语非常重要,以至于在Python 3中,它现在有了特殊的支持语法:def签名中单个*后面的每个参数都是仅关键字参数,也就是说,不能作为位置参数传递,而只能作为命名参数传递。所以在Python 3中,你可以将上面的代码编写为:

def f(*, foo=None, bar=None, **kwargs):
...etc...

实际上,在Python 3中,你甚至可以有可选的关键字参数(没有默认值的参数)。

然而,Python 2仍然有很长的生产寿命,所以最好忘记让你在Python 2中实现Python 3语言直接支持的重要设计思想的技术和习语!

这是另一种方法:

def my_func(arg1, arg2, arg3):
... so something ...


kwargs = {'arg1': 'Value One', 'arg2': 'Value Two', 'arg3': 'Value Three'}
# Now you can call the function with kwargs like this:


my_func(**kwargs)

如果你想把它和*args结合起来,你必须在定义的末尾保留*args和**kwargs。

所以:

def method(foo, bar=None, *args, **kwargs):
do_something_with(foo, bar)
some_other_function(*args, **kwargs)

既然在参数数量未知时使用**kwargs,为什么不这样做呢?

class Exampleclass(object):
def __init__(self, **kwargs):
for k in kwargs.keys():
if k in [acceptable_keys_list]:
self.__setattr__(k, kwargs[k])

我认为在Python中使用**kwargs的正确方法是使用字典方法setdefault,如下所示:

class ExampleClass:
def __init__(self, **kwargs):
kwargs.setdefault('val', value1)
kwargs.setdefault('val2', value2)

这样,如果用户在关键字args中传递'val'或'val2',它们将被使用;否则,将使用已设置的默认值。

跟进@srhegde使用< >强setattr < / >强的建议:

class ExampleClass(object):
__acceptable_keys_list = ['foo', 'bar']


def __init__(self, **kwargs):
[self.__setattr__(key, kwargs.get(key)) for key in self.__acceptable_keys_list]

当期望类拥有acceptable列表中的所有项时,这个变体非常有用。

@AbhinavGupta和@Steef建议使用update(),我发现它对处理大型参数列表非常有帮助:

args.update(kwargs)

如果我们想检查用户是否传递了任何虚假/不受支持的参数,该怎么办?@VinaySajip指出pop()可以用来迭代处理参数列表。那么,任何剩余的论点都是虚假的。好了。

下面是另一种可能的方法,它保持了使用update()的简单语法:

# kwargs = dictionary of user-supplied arguments
# args = dictionary containing default arguments


# Check that user hasn't given spurious arguments
unknown_args = user_args.keys() - default_args.keys()
if unknown_args:
raise TypeError('Unknown arguments: {}'.format(unknown_args))


# Update args to contain user-supplied arguments
args.update(kwargs)

unknown_args是一个set,包含默认值中不出现的参数名。

处理未知或多个参数的另一个简单解决方案可以是:

class ExampleClass(object):


def __init__(self, x, y, **kwargs):
self.x = x
self.y = y
self.attributes = kwargs


def SomeFunction(self):
if 'something' in self.attributes:
dosomething()

**kwargs允许自由添加任意数量的关键字参数。用户可能有一个键列表,可以为其设置默认值。但是为不确定数量的键设置默认值似乎是不必要的。最后,将键作为实例属性可能很重要。所以,我会这样做:

class Person(object):
listed_keys = ['name', 'age']


def __init__(self, **kwargs):
_dict = {}
# Set default values for listed keys
for item in self.listed_keys:
_dict[item] = 'default'
# Update the dictionary with all kwargs
_dict.update(kwargs)


# Have the keys of kwargs as instance attributes
self.__dict__.update(_dict)