我来自静态语言的背景。有人可以(最好通过示例)解释使用** kwargs胜过命名参数的现实世界的优势吗?
在我看来,这似乎只会使函数调用变得更加模糊。谢谢。
Answers:
您可能出于多种原因而接受几乎任意命名的参数-这就是 **kw
表格允许您执行的操作。
最常见的原因是将参数直接传递给您要包装的其他函数(装饰器是这种情况的一种情况,但FAR只是其中的一种!)-在这种情况下,**kw
松开了包装器和包装纸之间的耦合,因为包装程序不必知道或关心所有包装程序的参数。这是另一个完全不同的原因:
d = dict(a=1, b=2, c=3, d=4)
如果必须事先知道所有名称,那么显然不存在这种方法,对吗?顺便说一句,在适用的情况下,我更喜欢这种以键为文字字符串的字典的方式:
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
仅仅是因为后者的标点符号很重,因此可读性较差。
如果没有一个很好的接受理由**kwargs
,那么就不要接受:就这么简单。IOW,如果没有充分的理由允许调用者传递带有任意名称的额外的命名args,则不允许这种情况发生-只需避免**kw
在def
语句的函数签名末尾放置一个表单即可。
至于在调用中使用 **kw
,它使您可以将必须传递的确切命名参数集(独立于单个调用点)组合在一个dict中,然后将其与每个单独的调用值一起使用,然后在单个调用点上使用该dict。比较:
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()
即使只有两种可能性(也是最简单的一种!),但缺乏**kw
仍然导致第二种选择绝对站不住脚且令人无法忍受-想象一下,当有六种可能性时,它如何发挥作用,可能是在稍微丰富的互动中。没有这种**kw
生活,在这种情况下绝对是地狱!
有两种常见情况:
首先:您包装了另一个函数,该函数需要多个关键字参数,但是您只需要传递它们:
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
如果您事先不知道参数名称,那将是很好的选择。例如,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中使用的示例。我创建了一个**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')
解决方案是访问基础词典。
Div(**{'class':'foo'})
我并不是说这是该功能的“正确”用法。我的意思是,有各种各样无法预料的方式可以使用这种功能。
一个示例是实现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文档:partial相对地与此隐喻:
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
keywords
是字典,.copy()是否仅创建另一个指针?这意味着将fkeywords
其添加到原始字典中。我认为您想使用copy.deepcopy()创建字典的实际副本,不是吗?