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

Kek*_*koa 415 python kwargs

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

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

class ExampleClass:
    def __init__(self, **kwargs):
        self.val = kwargs['val']
        self.val2 = kwargs.get('val2')
Run Code Online (Sandbox Code Playgroud)

一个简单的问题,但我找不到好的资源.人们在我看过的代码中采用不同的方式,并且很难知道要使用什么.

bal*_*pha 430

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

self.val2 = kwargs.get('val2',"default value")
Run Code Online (Sandbox Code Playgroud)

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

def __init__(self, val2="default value", **kwargs):
Run Code Online (Sandbox Code Playgroud)

  • 您可以按您喜欢的任何顺序传递命名参数.如果你不使用名字,你只需要贴在这些位置上 - 在kwargs的情况下,你必须这样做.相反,使用命名参数而不是kwargs为你提供*不使用名称的额外*自由 - 然而,你必须保持秩序. (92认同)
  • 我喜欢仅为必需的参数使用位置参数,而对于可能指定或未指定的参数使用kwargs,但是有一个默认值是有帮助的.kwargs很好,因为你可以按照你选择的任何顺序提交你的args.位置论证不会给你这种自由. (14认同)
  • @Kekoa:您始终可以按您选择的任何顺序提交命名参数.您不必使用**kwargs来获得这种灵活性. (13认同)
  • pylint将它标记为在`__init __()`中使用kwargs的错误形式.有人可以解释为什么这是一个不值钱的违法行为吗? (13认同)
  • @hughdbrown可能是因为[一个简单的`self .__ dict__update(**kwargs)`可以重新定义方法并导致其他错误](http://stackoverflow.com/a/12191118/321973) (2认同)

Ale*_*lli 260

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

def f(**kwargs):
    foo = kwargs.pop('foo')
    bar = kwargs.pop('bar')
    ...etc...
Run Code Online (Sandbox Code Playgroud)

是相同的"

def f(foo=None, bar=None, **kwargs):
    ...etc...
Run Code Online (Sandbox Code Playgroud)

这不是真的.在后一种情况下,f可以称为f(23, 42),而前者的情况下接受命名的参数只有 -没有位置的调用.通常,您希望允许调用者具有最大的灵活性,因此第二种形式(因为大多数答案断言)更可取:但情况并非总是如此.当你接受许多可选参数时,通常只有少数几个被传递,这可能是一个很好的想法(避免在你的呼叫站点发生意外和不可读的代码!)来强制使用命名参数 - threading.Thread就是一个例子.第一种形式是如何在Python 2中实现它.

成语是如此重要,以至于在Python 3现在有特殊的支持语法:单后每个参数*def签名关键字只,也就是说,不能被作为位置参数传递,但只是作为一个命名的.所以在Python 3中你可以将上面的代码编写为:

def f(*, foo=None, bar=None, **kwargs):
    ...etc...
Run Code Online (Sandbox Code Playgroud)

实际上,在Python 3中,您甚至可以使用非关键字的参数,这些参数不是可选的(没有默认值的参数).

但是,Python 2仍然有很长一段时间的生产生活,所以最好不要忘记让你在Python 2中实现的技巧和习惯用法,这些想法在Python 3中直接支持语言!

  • @Alex Martelli:我没有找到一个答案,声称kwargs与命名论点相同,更不用说上级了.但对Py3k的良好讨论会发生变化,所以+1 (9认同)
  • @Alex Martelli:非常感谢您的回答,它让我了解到 python 3 允许强制关键字参数,缺乏这些参数通常会导致我的代码和函数的架构不令人满意。 (2认同)

Abh*_*pta 68

我建议这样的事情

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'}
Run Code Online (Sandbox Code Playgroud)

然后以您想要的任何方式使用值

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

  • 谢谢@AbhinavGupta!正是我想要的。刚刚添加了`for key in options:self .__ setattr __(key,options [key])`,我很高兴。**:)** (2认同)

Vin*_*jip 53

你做的

self.attribute = kwargs.pop('name', default_value)
Run Code Online (Sandbox Code Playgroud)

要么

self.attribute = kwargs.get('name', default_value)
Run Code Online (Sandbox Code Playgroud)

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

  • @Alan H.:如果在完成所有弹出之后在kwargs中遗留了任何东西,那么你就会得到虚假的价值. (13认同)
  • 您能否通过建议`.pop`来帮助您"检查是否发送任何虚假值"来澄清您的意思? (2认同)

S.L*_*ott 40

使用**kwargs和默认值很容易.但是,有时候,你不应该首先使用**kwargs.

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

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

以上是"为何费心?" 宣言.它是一样的

class ExampleClass( object ):
    def __init__(self, val="default1", val2="default2"):
        self.val = val
        self.val2 = val2
Run Code Online (Sandbox Code Playgroud)

当你使用**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" )
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢那个小脑筋急转弯!我一直在想,“但是你可以只使用 get 或 pop 来——哦,它们是相互依赖的……” (2认同)

小智 27

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

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


oro*_*aki 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)
Run Code Online (Sandbox Code Playgroud)


Ste*_*eef 11

你可以这样做

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


小智 11

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

class ExampleClass:
    def __init__(self, **kwargs):
        kwargs.setdefault('val', value1)
        kwargs.setdefault('val2', value2)
Run Code Online (Sandbox Code Playgroud)

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


reb*_*ard 10

跟进@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]
Run Code Online (Sandbox Code Playgroud)

当预期类具有acceptable列表中的所有项时,此变体很有用.

  • 这不是列表理解的用例,您应该在 init 方法中使用 for 循环。 (2认同)

Jen*_*man 5

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

所以:

def method(foo, bar=None, *args, **kwargs):
    do_something_with(foo, bar)
    some_other_function(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)