web-dev-qa-db-ja.com

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

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

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

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

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

391
Kekoa

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

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

しかし、特定のデフォルト値を持つ特定の引数を使用することを計画している場合、まず最初に名前付き引数を使用しないでください。

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

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

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

「と同じ」

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

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

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

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

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

ただし、Python 2には長年の生産的生命が残っているため、Python 2で実装できるテクニックとイディオムを忘れてくださいnotをお勧めしますPython 3の言語で直接サポートされているデザインアイデア!

256
Alex Martelli

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

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)dictionaryBの内容をdictionaryAに追加して重複するキーを上書きします。

63
Abhinav Gupta

あなたがやりたい

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

または

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

popを使用している場合は、偽の値が送信されているかどうかを確認し、適切な処置を取ります(存在する場合)。

52
Vinay Sajip

** 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" )
39
S.Lott

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

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

これは別のアプローチです:

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)
13
orokusaki

フォローアップ @ srhegde の使用の提案 setattr

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リストにすべての項目を持つことが予想される場合に役立ちます。

10
rebelliard

こんなことができる

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

Pythonで**kwargsをデフォルト値にするための適切な方法は、以下に示すように辞書メソッドsetdefaultを使用することです。

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

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

10
user1930143

これを* argsと組み合わせたい場合は、定義の最後に* argsと** kwargsを付けておく必要があります。

そう:

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

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

args.update(kwargs)

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

これを行うためのもう1つの方法があります。これは、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です。

1
user20160

** 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)
0
user3503692

未知または複数の引数を処理するためのもう1つの簡単な解決策は、次のとおりです。

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
tmsss