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


452

关于**kwargs默认值,在Python中使用的正确方法是什么?

kwargs返回一本字典,但是设置默认值的最佳方法是什么?我应该以字典的形式访问它吗?使用获取功能?

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

一个简单的问题,但是我找不到很好的资源。人们在我见过的代码中以不同的方式进行操作,很难知道该使用什么。

Answers:


468

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

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

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

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

16
我喜欢仅将位置参数用于必需的参数,将kwargs用于可能指定或可能未指定的参数,但是使用默认值会有所帮助。kwargs很不错,因为您可以按选择的任何顺序提交args。位置论据并不能给您那种自由。
Kekoa,

95
您可以按任意顺序传递命名参数。如果您不使用名称,则只需要坚持这些职位即可;对于kwargs,则必须这样做。相反,使用命名参数而不是kwargs给您提供了不使用名称的额外自由-但是,您必须保持顺序。
balpha

13
@Kekoa:您始终可以按选择的任何顺序提交命名参数。您不必使用** kwargs即可获得这种灵活性。
S.Lott

13
pylint将其标记为在中使用kwargs的错误形式__init__()。有人可以解释为什么这是一个皮棉价值的犯罪吗?
hughdbrown


271

虽然大多数答案都在说,例如,

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

是相同的”

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语言直接支持这些重要设计思想!


10
@Alex Martelli:我还没有找到一个单一的答案,声称kwargs与命名参数相同,更不用说高级了。但是有关Py3k的讨论变了,所以+1
balpha

1
@Alex Martelli:非常感谢您的回答,它使我了解到python 3允许使用强制性的关键字参数,而缺少该参数通常会导致我的代码和功能的体系结构不令人满意。
流量

78

我建议像这样

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)添加的内容dictionaryBdictionaryA覆盖任何重复的密钥。


2
谢谢@AbhinavGupta!正是我想要的。刚刚添加for key in options: self.__setattr__(key, options[key]),我很好。 :)
propjk007'1

53

你会的

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

要么

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

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


2
您能否通过建议.pop将“帮助您检查是否发送了任何虚假值”来澄清您的意思?
艾伦·H。

13
@Alan H .:如果在完成所有弹出操作后,kwarg中还剩下任何东西,那么您就得到了虚假的值。
Vinay Sajip,2011年

@VinaySajip:好吧,这是一个伟大的.pop点“与”不用彷徨,但我还是不明白为什么流行最好通过命名参数,除了迫使调用者不使用位置参数。
MestreLion'4

1
@MestreLion:这取决于您的API允许多少个关键字参数。我并不是说我的建议比命名参数要好,但是Python允许您捕获未命名参数kwargs是有原因的。
Vinay Sajip'4

因此,只需检查即可。如果键存在,pop是否返回字典值,否则返回返回default_value传递的值?并随后删除该密钥?
DanielMöller17年

42

使用** kwargs和默认值很容易。但是,有时候,您不应该一开始就使用** kwargs。

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

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" )

我喜欢那个小脑筋急转弯!我一直在想,“但是您可以只使用get或
pop-

28

由于**kwargs在参数个数未知时使用,为什么不这样做呢?

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

是的,这是优雅而强大的...不过,我不太确定acceptable_keys_list周围的方括号:我将其设为一个元组或列表,然后将这些方括号放在“ if”语句中
mike rodent

对于需要所有密钥的情况,我做了一些修改:stackoverflow.com/questions/1098549/…–
rebelliard

14

这是另一种方法:

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)

在Django CBV中使用了很多(例如get_form_kwargs())。ccbv.co.uk/projects/Django/1.5/django.views.generic.edit/...
trojjer

get_form()方法说明了如何通过遵循另一种方法(get_form_kwargs如上所述)来广泛地获取关键字参数。它实例如下形式:form_class(**self.get_form_kwargs())
trojjer 2013年

这样就很容易get_form_kwargs()在子类视图中进行覆盖,并根据特定的逻辑添加/删除kwarg。但这是针对Django教程的。
trojjer 2013年

12

我认为在**kwargs默认值上使用Python 的正确方法是使用dictionary方法setdefault,如下所示:

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

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


11

你可以做这样的事情

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

11

跟进@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列表中的所有项目时,此变体很有用。


1
那不是列表理解的用例,您应该在init方法中使用for循环。
ettanany

5

如果要将其与* args结合使用,则必须在定义末尾保留* args和** kwargs。

所以:

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

1

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

args.update(kwargs)

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

这是执行此操作的另一种可能方法,保留了using的简单语法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包含默认值中不出现的参数名称。


0

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

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()

0

** 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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.