为什么在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