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

在Python中使用**kwargs的正确方法,python,keyword-argument,Python,Keyword Argument,当涉及到默认值时,在Python中使用**kwargs的正确方法是什么 kwargs返回一个字典,但是设置默认值的最佳方法是什么,或者有没有?我是不是应该把它当作字典来查?使用get函数 class ExampleClass: def __init__(self, **kwargs): self.val = kwargs['val'] self.val2 = kwargs.get('val2') 一个简单的问题,但我找不到好的资源。人们用我见过的代码做这

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

kwargs
返回一个字典,但是设置默认值的最佳方法是什么,或者有没有?我是不是应该把它当作字典来查?使用get函数

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

一个简单的问题,但我找不到好的资源。人们用我见过的代码做这件事的方式不同,很难知道该用什么。

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

self.val2 = kwargs.get('val2',"default value")
但是,如果计划使用具有特定默认值的特定参数,为什么不首先使用命名参数

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

self.attribute = kwargs.pop('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']
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'}

使用**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
def f(foo=None, bar=None, **kwargs):
    ...etc...
当您使用**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" )

我建议这样做

class ExampleClass:
    def __init__(self, **kwargs):
        arguments = {'val':1, 'val2':2}
        arguments.update(kwargs)
        self.val = arguments['val']
        self.val2 = arguments['val2']
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...
“与”相同吗

事实并非如此。在后一种情况下,
f
可以被称为
f(23,42)
,而前一种情况只接受命名参数,不接受位置调用。通常,您希望允许调用者具有最大的灵活性,因此,正如大多数答案所断言的那样,第二种形式更可取:但情况并非总是如此。当您接受许多可选参数(通常只传递其中的少数参数)时,强制使用命名参数可能是一个很好的主意(避免在调用站点出现意外和无法读取的代码!)线程化就是一个例子。第一种形式是如何在Python2中实现它

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

def f(*, foo=None, bar=None, **kwargs):
    ...etc...
事实上,在Python3中,甚至可以有非可选的仅关键字参数(没有默认值的参数)


然而,Python2还有很长的生产寿命,因此最好不要忘记让您在Python2中实现Python3语言直接支持的重要设计思想的技术和习惯用法

还有另一种方法:

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

因此:


既然
**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中使用默认值的正确方法是使用dictionary方法
setdefault
,如下所示:

class ExampleClass:
    def __init__(self, **kwargs):
        kwargs.setdefault('val', value1)
        kwargs.setdefault('val2', value2)
这样,如果用户在关键字
args
中传递“val”或“val2”,则将使用它们;否则,将使用已设置的默认值。

跟进使用建议:


当类希望在我们的
可接受的
列表中包含所有项时,此变量非常有用。

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

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

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


unknown\u args
是一个
集合
,包含默认值中未出现的参数名称。

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

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)

我喜欢只对必需的参数使用位置参数,对可能指定或可能未指定的参数使用kwargs,但使用默认值会有所帮助。kwargs很好,因为你可以按照你选择的任何顺序提交你的args。位置参数不能给你自由。你可以按你喜欢的顺序传递命名参数。如果你不使用名字,你只需要贴在位置上——在kwargs的情况下,你必须这样做。相反,使用命名参数而不是kwargs会给您额外的不使用名称的自由,但是,您必须遵守顺序。@Kekoa:您始终可以按照您选择的任何顺序提交命名参数。您不必使用**kwargs来获得这种灵活性。pylint将在
中使用kwargs标记为坏形式。有人能解释为什么这是一个值得一提的违规行为吗?@hughdbrown可能是因为@Alex Martelli:我还没有找到一个答案声称kwargs与命名参数相同,更不用说更高级了。但好的话语对Py3k的改变,也是如此+1@Alex马尔泰利:非常感谢你的帮助
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()
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)