web-dev-qa-db-ja.com

Pythonで** kwargsを使用する理由名前付き引数を使用する場合と比べて、実際にはどのような利点がありますか?

私は静的言語のバックグラウンドを持っています。誰かが(理想的には例を通して)現実の世界を説明できますか名前付き引数に対して** kwargsを使用する利点

私にとっては、関数呼び出しをあいまいにするだけのようです。ありがとう。

63
meppum

実際の例:

デコレータ-通常、これらは一般的なものであるため、引数を事前に指定することはできません。

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

未知の数のキーワード引数で魔法をかけたい場所。 DjangoのORMはそれを行います、例えば:

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

一連の理由により、ほぼ任意の名前付き引数を受け入れたい場合があります。これが**kwフォームで可能です。

最も一般的な理由は、ラップしている他の関数に引数を渡すことです(デコレータはこの1つのケースですが、FARは1つだけです)。この場合、**kwは、ラッパーはラッパーのすべての引数を認識したり気にする必要がないため、ラッパーとラッパー。別の理由は次のとおりです。

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

すべての名前を事前に知っておく必要がある場合、明らかにこのアプローチは存在し得ませんでしたよね?ところで、該当する場合は、キーがリテラル文字列であるディクテーションを作成するこの方法を私は非常に好みます:

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

後者は句読点が多く、読みにくいためです。

**kwargsを受け入れる優れた理由が当てはまらない場合は、受け入れないでください。これは非常に簡単です。 IOW、呼び出し側が任意の名前で追加の名前付き引数を渡すことを許可する正当な理由がない場合は、それを許可しないでください-defステートメントの関数のシグネチャの最後に**kwフォームを配置しないでください。

sing**kwの呼び出しでは、単一の呼び出しポイントとは無関係に、渡す必要のある名前付き引数の正確なセットを、対応する値とともに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()

たった2つの可能性がある(そして非常に単純な種類の)場合でも、**kwの欠如は、2番目のオプションを完全に受け入れがたく、耐えられないものにすることを先導しています。少し豊かな相互作用... **kwなしでは、そのような状況下では人生は絶対的な地獄です!

60
Alex Martelli

**kwargs(および*args)を使用するもう1つの理由は、サブクラスの既存のメソッドを拡張する場合です。既存のすべての引数をスーパークラスのメソッドに渡したいが、シグネチャが将来のバージョンで変更されても、クラスが引き続き機能するようにしたい場合:

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

2つの一般的なケースがあります。

最初に、いくつかのキーワード引数を取る別の関数をラップしていますが、それらを渡すだけです。

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

2番目:たとえば、オブジェクトに属性を設定するために、任意のキーワード引数を受け入れます。

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'
11
Ned Batchelder

**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}
4

以下は、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'})

これが機能の「正しい」使用法であると言っているのではありません。私が言っているのは、このような機能を使用できるあらゆる種類の予期しない方法があるということです。

3
Imagist

そして、ここに別の典型的な例があります:

MESSAGE = "Lo and behold! A message {message!r} came from {object_} with data {data!r}."

def proclaim(object_, message, data):
    print(MESSAGE.format(**locals()))
1
ilya n.

1つの例は 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からのものです:パーシャルはこの実装と「比較的同等」です:

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
0
Dustin Getz