为什么在python中使用**kwargs?使用命名参数有哪些现实世界的优势?

mep*_*pum 64 python kwargs

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

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

Ale*_*lli 61

出于一系列原因,您可能希望接受几乎任意的命名参数 - 这就是**kw表单允许您执行的操作.

最常见的原因是将参数正确传递给你正在包装的其他函数(装饰器就是这种情况的一种情况,但只有一个FAR!) - 在这种情况下,**kw松开包装器和包装器之间的耦合,如包装器不必知道或关心所有wrappee的参数.这是另一个完全不同的原因:

d = dict(a=1, b=2, c=3, d=4)
Run Code Online (Sandbox Code Playgroud)

如果所有的名字都必须事先知道,那么这种方法显然不可能存在,对吧?顺便说一句,在适用的情况下,我更喜欢这种制作一个字符串的字符串:

d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
Run Code Online (Sandbox Code Playgroud)

仅仅因为后者非常标点符号,因此可读性较差.

如果没有一个接受的优秀理由**kwargs适用,那就不要接受它:就这么简单.IOW,如果没有充分的理由允许调用者传递具有任意名称的额外命名args,请不要允许这种情况发生 - 只需避免**kwdef语句的函数签名末尾放置一个表单.

至于在调用中的使用 **kw,它允许你将一个必须传递的确切命名参数集合在一起,每个都有相应的值,在dict中,独立于单个调用点,然后在单个调用点使用该dict.相比:

if x: kw['x'] = x
if y: kw['y'] = y
f(**kw)
Run Code Online (Sandbox Code Playgroud)

至:

if x:
  if y:
    f(x=x, y=y)
  else:
    f(x=x)
else:
  if y:
    f(y=y)
  else:
    f()
Run Code Online (Sandbox Code Playgroud)

即使只有两种可能性(也是最简单的一种!),缺乏**kw可能性使第二种选择绝对难以忍受和无法容忍 - 想象一下当有六种可能性时它会如何发挥作用,可能是稍微丰富的交互.没有**kw,在这种情况下生活将是绝对的地狱!


Dan*_*man 40

您可能想要使用**kwargs(和*args)的另一个原因是您在子类中扩展现有方法.您希望将所有现有参数传递给超类的方法,但是要确保即使签名在将来版本中更改,您的类仍会继续工作:

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


Cat*_*lus 38

现实世界的例子:

装饰器 - 它们通常是通用的,因此您无法预先指定参数:

def decorator(old):
    def new(*args, **kwargs):
        # ...
        return old(*args, **kwargs)
    return new
Run Code Online (Sandbox Code Playgroud)

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

Model.objects.filter(foo__lt = 4, bar__iexact = 'bar')
Run Code Online (Sandbox Code Playgroud)

  • “想要施展魔法吗?”是什么意思?这里的_magic_是什么意思?另外,我将_magic_与糟糕的设计联系起来。Django案例设计合理吗? (2认同)

Ned*_*der 11

有两种常见情况:

第一:你正在包装另一个带有许多关键字参数的函数,但你只是要传递它们:

def my_wrapper(a, b, **kwargs):
    do_something_first(a, b)
    the_real_function(**kwargs)
Run Code Online (Sandbox Code Playgroud)

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

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'
Run Code Online (Sandbox Code Playgroud)


Cri*_*itu 5

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

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

  • 如果您想将大量参数传递给另一个您不一定知道选项的函数,则通常会使用它。例如,specialplot(a,**kwargs) 可以将 kwargs 中的绘图选项传递给接受这些选项作为命名参数的通用绘图函数。 (2认同)
  • 尽管我认为这种风格很糟糕,因为它阻碍了内省。 (2认同)