我来自静态语言的背景。有人可以解释(理想情况下通过实例)真正的世界使用** kwargs优于命名参数?为什么在Python中使用** kwargs?与使用命名参数相比,有哪些真实世界的优点?
对我来说,它似乎只是使函数调用更模糊。谢谢。
我来自静态语言的背景。有人可以解释(理想情况下通过实例)真正的世界使用** kwargs优于命名参数?为什么在Python中使用** 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')
**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}
一个示例实施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文档:局部是 '相对等效' 来这个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
这里,因为'keywords'是一个字典,不.copy()只是创建另一个指针?这意味着'fkeywords'被添加到原始字典中。我想你会想使用copy.deepcopy()来创建字典的实际副本,不是吗? – aeroNotAuto 2011-11-09 17:44:34
*'currying'*是*'部分参数绑定'的街道名称* – smci 2013-08-16 09:36:34
由于一系列原因,您可能想要接受几乎任意命名的参数 - 这就是**kw
表单允许您执行的操作。
最常见的原因是将参数直接传递给你正在包装的其他函数(装饰器是这种情况的一种情况,但FAR来自唯一的一种情况!) - 在这种情况下,**kw
放松了包装和wrappee,因为包装者不必知道或关心所有wrappee的论点。下面是另一个完全不同的原因:
d = dict(a=1, b=2, c=3, d=4)
如果所有的名字不得不提前知道的话,显然这种做法简直不存在的,对不对?而且顺便说一句,在适用时,我更喜欢做一个字典的键是文字字符串,以这种方式:
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()
即使只有两个可能性(!和非常简单的一种),由于缺乏**kw
时已位于使得第二个选项绝对站不住脚的,不能容忍的 - 只是想象一下当它出现六种可能性时,它会发生什么,可能是稍微更丰富的互动......如果没有**kw
,在这种情况下生活将是绝对的地狱!
+1 - 如果您习惯于(说)java,那么可以做到这一点,但这种做法有一定的范式转变。 – ConcernedOfTunbridgeWells 2009-09-12 20:29:22
有两种常见的情况:
第一条:您是包装另外的功能,这需要一些关键字参数,但你只是要通过他们:
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
另一个原因(和*args
)是如果你要扩展一个子类中的现有方法。您想通过现有的所有参数到父类的方法,但要确保你的类继续工作,即使在未来的版本中签名的变化:
class MySubclass(Superclass):
def __init__(self, *args, **kwargs):
self.myvalue = kwargs.pop('myvalue', None)
super(MySubclass, self).__init__(*args, **kwargs)
+1:非常非常频繁地这样做。 – 2009-09-13 12:54:10
这里有一个例子,我在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'})
我并不是说这是该功能的“正确”用法。我所说的是,有各种不可预知的方式可以使用这样的功能。
这里是另一个典型的例子:
MESSAGE = "Lo and behold! A message {message!r} came from {object_} with data {data!r}."
def proclaim(object_, message, data):
print(MESSAGE.format(**locals()))
用C可变参数的想法 - 有时你无法知道你的论点是什么。 – 2009-09-13 05:48:23
请不要使用“现实世界”这个词 - 它是有争议的。它说“到目前为止,我所见过的所有例子都是人为和无用的,我的应用程序是现实世界,你的例子是一个幻想世界。”请改变你的问题以消除“真实世界”。 – 2009-09-13 12:53:37
只有喜欢争论的人会认为我的问题是争议性的。 – meppum 2009-10-02 02:38:20