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

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

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

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


当前回答

既然**kwargs是在参数数量未知的情况下使用的,为什么不这样做呢?

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

其他回答

@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是一个包含不出现在默认值中的参数名的集合。

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

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

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

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

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

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

是"the same as"

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

this is not true. In the latter case, f can be called as f(23, 42), while the former case accepts named arguments only -- no positional calls. Often you want to allow the caller maximum flexibility and therefore the second form, as most answers assert, is preferable: but that is not always the case. When you accept many optional parameters of which typically only a few are passed, it may be an excellent idea (avoiding accidents and unreadable code at your call sites!) to force the use of named arguments -- threading.Thread is an example. The first form is how you implement that in Python 2.

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

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

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

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

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

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

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

你会做

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

or

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

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