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

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

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

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

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

What is the proper way to use **kwargs in Python when it comes to default values?

kwargs returns a dictionary, but what is the best way to set default values, or is there one? Should I just access it as a dictionary? Use get function?

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

A simple question, but one that I can’t find good resources on. People do it different ways in code that I’ve seen and it’s hard to know what to use.


回答 0

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

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

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

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

You can pass a default value to get() for keys that are not in the dictionary:

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

However, if you plan on using a particular argument with a particular default value, why not use named arguments in the first place?

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

回答 1

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

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

While most answers are saying that, e.g.,

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

is “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.

The idiom is so important that in Python 3 it now has special supporting syntax: every argument after a single * in the def signature is keyword-only, that is, cannot be passed as a positional argument, but only as a named one. So in Python 3 you could code the above as:

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

Indeed, in Python 3 you can even have keyword-only arguments that aren’t optional (ones without a default value).

However, Python 2 still has long years of productive life ahead, so it’s better to not forget the techniques and idioms that let you implement in Python 2 important design ideas that are directly supported in the language in Python 3!


回答 2

我建议像这样

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覆盖任何重复的密钥。

I suggest something like this

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'}

And then use the values any way you want

dictionaryA.update(dictionaryB) adds the contents of dictionaryB to dictionaryA overwriting any duplicate keys.


回答 3

你会的

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

要么

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

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

You’d do

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

or

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

If you use pop, then you can check if there are any spurious values sent, and take the appropriate action (if any).


回答 4

使用** 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" )

Using **kwargs and default values is easy. Sometimes, however, you shouldn’t be using **kwargs in the first place.

In this case, we’re not really making best use of **kwargs.

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

The above is a “why bother?” declaration. It is the same as

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

When you’re using **kwargs, you mean that a keyword is not just optional, but conditional. There are more complex rules than simple default values.

When you’re using **kwargs, you usually mean something more like the following, where simple defaults don’t apply.

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

回答 5

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

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

Since **kwargs is used when the number of arguments is unknown, why not doing this?

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

回答 6

这是另一种方法:

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)

Here’s another approach:

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)

回答 7

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

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

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

I think the proper way to use **kwargs in Python when it comes to default values is to use the dictionary method setdefault, as given below:

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

In this way, if a user passes ‘val’ or ‘val2’ in the keyword args, they will be used; otherwise, the default values that have been set will be used.


回答 8

你可以做这样的事情

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

You could do something like this

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

回答 9

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

Following up on @srhegde suggestion of using 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]

This variant is useful when the class is expected to have all of the items in our acceptable list.


回答 10

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

所以:

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

If you want to combine this with *args you have to keep *args and **kwargs at the end of the definition.

So:

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

回答 11

@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包含默认值中不出现的参数名称。

@AbhinavGupta and @Steef suggested using update(), which I found very helpful for processing large argument lists:

args.update(kwargs)

What if we want to check that the user hasn’t passed any spurious/unsupported arguments? @VinaySajip pointed out that pop() can be used to iteratively process the list of arguments. Then, any leftover arguments are spurious. Nice.

Here’s another possible way to do this, which keeps the simple syntax of 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 is a set containing the names of arguments that don’t occur in the defaults.


回答 12

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

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

Another simple solution for processing unknown or multiple arguments can be:

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

回答 13

** 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 gives the freedom to add any number of keyword arguments. One may have a list of keys for which he can set default values. But setting default values for an indefinite number of keys seems unnecessary. Finally, it may be important to have the keys as instance attributes. So, I would do this as follows:

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)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。