Pythonで** kwargsを使用する適切な方法


454

適切な使用方法は何ですか **kwargsデフォルト値に関して、Pythonでですか?

kwargs辞書を返しますが、デフォルト値を設定する最良の方法は何ですか、またはそれはありますか?辞書としてアクセスするだけですか?get関数を使用しますか?

class ExampleClass:
    def __init__(self, **kwargs):
        self.val = kwargs['val']
        self.val2 = kwargs.get('val2')

簡単な質問ですが、良いリソースを見つけることができません。人々は私が見たコードでそれをさまざまな方法で行っており、何を使用するかを知るのは困難です。

回答:


468

get()辞書にないキーのデフォルト値を渡すことができます:

self.val2 = kwargs.get('val2',"default value")

ただし、特定のデフォルト値で特定の引数を使用する場合は、最初に名前付き引数を使用しないのはなぜですか?

def __init__(self, val2="default value", **kwargs):

16
必要な引数にのみ位置引数を使用し、指定できる引数と指定できない引数にkwargsを使用したいのですが、デフォルト値を使用すると便利です。引数は任意の順序で送信できるため、kwargsは便利です。位置的な議論はあなたにその自由を与えません。
Kekoa

95
名前付き引数は、好きな順序で渡すことができます。名前を使用しない場合にのみ、位置を守る必要があります。kwargsの場合は、名前を使用する必要があります。むしろ、kwargsとは対照的に名前付き引数を使用すると、名前を使用しないという追加の自由が得られます。ただし、順序を維持する必要があります。
balpha 2009

13
@Kekoa:名前付き引数は、選択した任意の順序でいつでも送信できます。この柔軟性を得るために** kwargsを使用する必要はありません。
S.Lott、2009

13
pylintは、でkwargsを使用するのが不適切な形式であることを示し__init__()ます。これが糸くずの出ない価値のある違反である理由を誰かが説明できますか?
hughdbrown 09年


271

ほとんどの答えはそれを言っているが、例えば、

def f(**kwargs):
    foo = kwargs.pop('foo')
    bar = kwargs.pop('bar')
    ...etc...

「と同じ」

def f(foo=None, bar=None, **kwargs):
    ...etc...

本当じゃない。後者の場合fはとして呼び出すことができますがf(23, 42)、前者の場合は名前付き引数のみを受け入れます-位置呼び出しは行いません。多くの場合、発信者に最大限の柔軟性を許可する必要があるため、ほとんどの回答が主張するように、2番目の形式が望ましいですが、常にそうであるとは限りません。多くのオプションのパラメーターを受け入れる場合、通常はいくつかしか渡されませんが、名前付き引数の使用を強制することは優れたアイデアである可能性があります(事故や読み取り不能なコードを回避する!)threading.Thread。最初の形式は、Python 2でそれを実装する方法です。

このイディオムは非常に重要なので、Python 3では特別なサポート構文が追加さ*れました。defシグネチャ内のシングルの後のすべての引数はキーワードのみです。つまり、位置引数として渡すことはできず、名前付き引数としてのみ渡すことができます。したがって、Python 3では、上記を次のようにコーディングできます。

def f(*, foo=None, bar=None, **kwargs):
    ...etc...

実際、Python 3では、オプションではないキーワードのみの引数(デフォルト値のない引数)を使用することもできます。

ただし、Python 2にはまだ長年の生産性があるため、Python 3の言語で直接サポートされている重要な設計アイデアをPython 2に実装できるようにする手法とイディオムを忘れないようにしてください。


10
@Alex Martelli:kwargsが名前付き引数と同一であると主張する単一の回答は見つかりませんでした。しかし、Py3kの変更についての良い談話なので、+ 1
balpha 2009

1
@Alex Martelli:あなたの答えに感謝します。Python3は必須のキーワード引数を許可することを学びました。これがないと、コードと関数のアーキテクチャが不十分になることがよくありました。
FloW

78

私はこのようなものを提案します

def testFunc( **kwargs ):
    options = {
            'option1' : 'default_value1',
            'option2' : 'default_value2',
            'option3' : 'default_value3', }

    options.update(kwargs)
    print options

testFunc( option1='new_value1', option3='new_value3' )
# {'option2': 'default_value2', 'option3': 'new_value3', 'option1': 'new_value1'}

testFunc( option2='new_value2' )
# {'option1': 'default_value1', 'option3': 'default_value3', 'option2': 'new_value2'}

そして、値を好きなように使用します

dictionaryA.update(dictionaryB)の内容を追加して、重複するキーdictionaryBdictionaryA上書きします。


2
ありがとう@AbhinavGupta!まさに私が探していたもの。追加for key in options: self.__setattr__(key, options[key])しただけでいいです。 :)
propjk007

53

あなたがします

self.attribute = kwargs.pop('name', default_value)

または

self.attribute = kwargs.get('name', default_value)

を使用popする場合は、偽の値が送信されていないかどうかを確認し、適切なアクション(ある場合)を実行できます。


2
.pop「送信された偽の値があるかどうかを確認する」のに役立つと示唆することで、あなたが何を意味するのかを明確にできますか?
アランH.

13
@Alan H .:すべてのポップが完了した後でクワーグに何か残っている場合、偽の値を持っています。
Vinay Sajip、

@VinaySajip:わかりました。これは.pop "vs" .getの優れた点ですが、名前付き引数よりもpopが望ましい理由はまだわかりません。
MestreLion

1
@MestreLion:APIで許可されているキーワード引数の数によって異なります。私の提案が名前付き引数より優れているとは主張していませんが、Pythonではkwargs理由のない名前付き引数を取り込むことができます。
Vinay Sajip

だから、ただチェックしています。キーが存在する場合、popはディクショナリ値を返しますdefault_valueか?その後、そのキーを削除しますか?
ダニエル・メーラー

42

** kwargsとデフォルト値を使用するのは簡単です。ただし、最初に** kwargsを使用するべきではない場合もあります。

この場合、実際には** kwargsを最大限に活用していません。

class ExampleClass( object ):
    def __init__(self, **kwargs):
        self.val = kwargs.get('val',"default1")
        self.val2 = kwargs.get('val2',"default2")

上記は「なぜわざわざ?」宣言。と同じです

class ExampleClass( object ):
    def __init__(self, val="default1", val2="default2"):
        self.val = val
        self.val2 = val2

** kwargsを使用している場合、キーワードは単なるオプションではなく、条件付きであることを意味します。単純なデフォルト値よりも複雑なルールがあります。

** kwargsを使用している場合、通常は次のようなものを意味します。単純なデフォルトは適用されません。

class ExampleClass( object ):
    def __init__(self, **kwargs):
        self.val = "default1"
        self.val2 = "default2"
        if "val" in kwargs:
            self.val = kwargs["val"]
            self.val2 = 2*self.val
        elif "val2" in kwargs:
            self.val2 = kwargs["val2"]
            self.val = self.val2 / 2
        else:
            raise TypeError( "must provide val= or val2= parameter values" )

私はその小さな頭の体操が好きです!私は「でも、getまたはpopを使用することができます-ああ、それらは
相互

28

**kwargs引数の数が不明な場合に使用されるので、なぜこれを行わないのですか?

class Exampleclass(object):
  def __init__(self, **kwargs):
    for k in kwargs.keys():
       if k in [acceptable_keys_list]:
          self.__setattr__(k, kwargs[k])

はい、これは、エレガントかつ強力です...すぎないことを確認acceptable_keys_list周りの角括弧についてかかわら:私はこのタプルまたはリスト行い、その後、「IF」文の中で、これらのブラケットをドロップしたい
マイクげっ歯類

すべてのキーが期待されているとき、私は少しのケースのためにこれを変更:stackoverflow.com/questions/1098549/...
rebelliard

14

ここに別のアプローチがあります:

def my_func(arg1, arg2, arg3):
    ... so something ...

kwargs = {'arg1': 'Value One', 'arg2': 'Value Two', 'arg3': 'Value Three'}
# Now you can call the function with kwargs like this:

my_func(**kwargs)

Django CBVでよく使用されます(例:)get_form_kwargs()ccbv.co.uk/projects/Django/1.5/django.views.generic.edit/...
trojjer

このget_form()メソッドは、(get_form_kwargs上記のように)別のメソッドに延期することによってキーワード引数を広範囲に取得する方法を示しています。次のようにフォームをインスタンス化しますform_class(**self.get_form_kwargs())
trojjer 2013年

その後get_form_kwargs()、サブクラスビューでオーバーライドし、特定のロジックに基づいてクワルグを追加/削除するのは簡単です。しかし、それはDjangoチュートリアル用です。
trojjer 2013年

12

**kwargsPythonでデフォルト値を使用する適切な方法はsetdefault、以下に示すように、辞書メソッドを使用することだと思います。

class ExampleClass:
    def __init__(self, **kwargs):
        kwargs.setdefault('val', value1)
        kwargs.setdefault('val2', value2)

このように、ユーザーがキーワードargsに「val」または「val2」を渡すと、それらが使用されます。それ以外の場合は、設定されているデフォルト値が使用されます。


11

あなたはこのようなことをすることができます

class ExampleClass:
    def __init__(self, **kwargs):
        arguments = {'val':1, 'val2':2}
        arguments.update(kwargs)
        self.val = arguments['val']
        self.val2 = arguments['val2']

11

setattrの使用に関する@srhegde提案のフォローアップ:

class ExampleClass(object):
    __acceptable_keys_list = ['foo', 'bar']

    def __init__(self, **kwargs):
        [self.__setattr__(key, kwargs.get(key)) for key in self.__acceptable_keys_list]

このバリアントは、クラスがacceptableリスト内のすべての項目を持つことが期待される場合に役立ちます。


1
これはリスト内包の使用例ではありません。initメソッドでforループを使用する必要があります。
ettanany

5

これを* argsと組み合わせる場合は、定義の最後に* argsと** kwargsを保持する必要があります。

そう:

def method(foo, bar=None, *args, **kwargs):
    do_something_with(foo, bar)
    some_other_function(*args, **kwargs)

1

@AbhinavGuptaと@Steefはの使用を提案しましたupdate()。これは、大きな引数リストを処理するのに非常に役立ちました。

args.update(kwargs)

ユーザーが偽の/サポートされていない引数を渡していないことを確認したい場合はどうなりますか?@VinaySajipはpop()、引数のリストを繰り返し処理するために使用できることを指摘しました。次に、残りの引数は偽です。いいね。

これを行う別の可能な方法は次のとおりです。これにより、使用する単純な構文が維持されますupdate()

# kwargs = dictionary of user-supplied arguments
# args = dictionary containing default arguments

# Check that user hasn't given spurious arguments
unknown_args = user_args.keys() - default_args.keys()
if unknown_args:
    raise TypeError('Unknown arguments: {}'.format(unknown_args))

# Update args to contain user-supplied arguments
args.update(kwargs)

unknown_argsされset、デフォルトでは発生しません。引数の名前を含みます。


0

不明な引数または複数の引数を処理するための別の簡単な解決策は、

class ExampleClass(object):

    def __init__(self, x, y, **kwargs):
      self.x = x
      self.y = y
      self.attributes = kwargs

    def SomeFunction(self):
      if 'something' in self.attributes:
        dosomething()

0

** kwargsは、任意の数のキーワード引数を自由に追加できます。デフォルト値を設定できるキーのリストがあるかもしれません。ただし、不特定の数のキーのデフォルト値を設定する必要はないようです。最後に、キーをインスタンス属性として持つことが重要な場合があります。だから、私はこれを次のように行います:

class Person(object):
listed_keys = ['name', 'age']

def __init__(self, **kwargs):
    _dict = {}
    # Set default values for listed keys
    for item in self.listed_keys: 
        _dict[item] = 'default'
    # Update the dictionary with all kwargs
    _dict.update(kwargs)

    # Have the keys of kwargs as instance attributes
    self.__dict__.update(_dict)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.