79

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

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

4

8 に答える 8

70

一連の理由から、ほぼ任意の名前付き引数を受け入れたいと思うかもしれませんが、それが**kwフォームで可能です。

最も一般的な理由は、ラップしている他の関数に直接引数を渡すことです (デコレータはこれの 1 つのケースですが、唯一のケースとはかけ離れています!) -- この場合、**kwラッパーとラップピーの間の結合を緩めます。ラッパーは、ラッパーのすべての引数を認識したり気にしたりする必要はありません。別の、まったく異なる理由を次に示します。

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

すべての名前を事前に知っておく必要がある場合、明らかにこのアプローチは存在しませんよね? ところで、該当する場合は、キーがリテラル文字列である dict を作成するこの方法を次のように好みます。

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

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

受け入れる優れた理由がどれも**kwargs当てはまらない場合は、受け入れないでください。それは簡単なことです。IOW、呼び出し元が任意の名前で追加の名前付き引数を渡すことを許可する正当な理由がない場合は、それを許可しないでください-ステートメント**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()

可能性が 2 つしかない (そして最も単純な種類の) 場合でも**kw、2 番目のオプションがまったく受け入れられず、耐え難いものになっていることはすでに明らかです。 . これがなければ**kw、このような状況下での生活は絶対に地獄です!

于 2009-09-12T18:52:09.237 に答える
44

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

class MySubclass(Superclass):
    def __init__(self, *args, **kwargs):
        self.myvalue = kwargs.pop('myvalue', None)
        super(MySubclass, self).__init__(*args, **kwargs)
于 2009-09-12T19:15:33.167 に答える
40

実際の例:

デコレータ - 通常は汎用的なため、事前に引数を指定することはできません:

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

不明な数のキーワード引数で魔法をかけたい場所。Django の ORM はそれを行います。

Model.objects.filter(foo__lt = 4, bar__iexact = 'bar')
于 2009-09-12T18:50:27.017 に答える
14

次の 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'
于 2009-09-12T18:56:22.170 に答える
4

**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}
于 2009-09-12T18:45:42.730 に答える
3

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'})

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

于 2009-09-12T19:49:47.600 に答える
1

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

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

def proclaim(object_, message, data):
    print(MESSAGE.format(**locals()))
于 2009-09-13T05:30:16.683 に答える
0

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 からのものです: 部分的なものは、この 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
于 2009-09-12T18:50:59.567 に答える