为什么在Python中使用** kwargs? 与使用命名参数相比,真正的世界优势是什么?

我来自静态语言的背景。 有人可以解释(理想情况下通过示例) 使用** kwargs超过命名参数的真实世界的优势

对我来说,似乎只是使函数调用更模糊。 谢谢。

真实世界的例子:

装饰者 – 他们通常是通用的,所以你不能预先指定参数:

def decorator(old): def new(*args, **kwargs): # ... return old(*args, **kwargs) return new 

您想要使用未知数量的关键字参数进行魔术的地方。 Django的ORM可以做到这一点,例如:

 Model.objects.filter(foo__lt = 4, bar__iexact = 'bar') 

您可能想要接受几乎任意命名的参数,这是由于一系列的原因 – 这就是**kw表格允许您执行的操作。

最常见的原因是将parameter passing给你正在包装的其他函数(装饰器是这种情况的一种情况,但从唯一的一个!) – 在这种情况下, **kw放松包装和因为包装者不需要知道或关心所有包装的论点。 这是另一个完全不同的原因:

 d = dict(a=1, b=2, c=3, d=4) 

如果所有的名字都要提前知道,那么显然这种做法根本就不存在,对吗? 顺便说一句,如果适用的话,我更喜欢这种方式来做一个字典的键是文字string:

 d = {'a': 1, 'b': 2, 'c': 3, 'd': 4} 

只是因为后者的标点符号很重,因此可读性较差。

如果没有接受**kwargs的优秀理由适用,那就不要接受:简单如此。 IOW,如果没有充分的理由允许调用者使用任意名称传递额外的命名参数,则不要允许发生这种情况 – 只要避免在def语句的函数签名末尾放置一个**kw表单即可。

至于在调用中使用 **kw ,可以让你将一组必须通过的命名参数(每个都带有相应的值)放在一个字典中,与单个调用点无关,然后在单个调用中使用该字典点。 比较:

 if x: kw['x'] = x if y: kw['y'] = y f(**kw) 

至:

 if x: if y: f(x=x, y=y) else: f(x=x) else: if y: f(y=y) else: f() 

即使只有两种可能性(也是最简单的一种),第二种select绝对是站不住脚,难以容忍的 – 想象一下,当有六种可能性时,它是如何发挥作用的,可能稍微富有些互动…没有**kw ,在这种情况下生活就绝对是地狱!

你可能想要使用**kwargs (和*args )的另一个原因是如果你在一个子类中扩展现有的方法。 你想把所有现有的parameter passing给超类的方法,但是要确保你的类继续工作,即使签名在未来的版本中改变:

 class MySubclass(Superclass): def __init__(self, *args, **kwargs): self.myvalue = kwargs.pop('myvalue', None) super(MySubclass, self).__init__(*args, **kwargs) 

有两种常见的情况:

首先:你正在包装另外一个需要多个关键字参数的函数,但是你只需要传递它们:

 def my_wrapper(a, b, **kwargs): do_something_first(a, b) the_real_function(**kwargs) 

第二:您愿意接受任何关键字参数,例如,在对象上设置属性:

 class OpenEndedObject: def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) foo = OpenEndedObject(a=1, foo='bar') assert foo.a == 1 assert foo.foo == 'bar' 

**kwargs如果你事先不知道参数的名字, **kwargs是很好的。 例如, dict构造函数使用它们初始化新字典的键。

 dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2) 
 In [3]: dict(one=1, two=2) Out[3]: {'one': 1, 'two': 2} 

下面是一个例子,我用在CGI Python中。 我创build了一个将**kwargs__init__函数的类。 这让我在服务器端用类来模拟DOM:

 document = Document() document.add_stylesheet('style.css') document.append(Div(H1('Imagist\'s Page Title'), id = 'header')) document.append(Div(id='body')) 

唯一的问题是你不能做以下事情,因为class是一个Python关键字。

 Div(class = 'foo') 

解决scheme是访问底层字典。

 Div(**{'class':'foo'}) 

我并不是说这是该function的“正确”用法。 我所说的是有各种各样的不可思议的方式可以使用这样的function。

这里还有一个典型的例子:

 MESSAGE = "Lo and behold! A message {message!r} came from {object_} with data {data!r}." def proclaim(object_, message, data): print(MESSAGE.format(**locals())) 

一个例子是实现python-argument-binders ,像这样使用:

 >>> from functools import partial >>> def f(a, b): ... return a+b >>> p = partial(f, 1, 2) >>> p() 3 >>> p2 = partial(f, 1) >>> p2(7) 8 

这是从functools.partial python docs:部分是相对等效的这个impl:

 def partial(func, *args, **keywords): def newfunc(*fargs, **fkeywords): newkeywords = keywords.copy() newkeywords.update(fkeywords) return func(*(args + fargs), **newkeywords) newfunc.func = func newfunc.args = args newfunc.keywords = keywords return newfunc