Правильный способ использования * * kwargs в Python


что такое правильный способ, чтобы использовать **kwargs в Python, когда дело доходит до значений по умолчанию?

kwargs возвращает словарь, но каков наилучший способ установить значения по умолчанию, или есть один? Должен ли я просто получить доступ к нему как к словарю? Использовать функции get?

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

простой вопрос, но я не могу найти хорошие ресурсы. Люди делают это по-разному в коде, который я видел, и трудно знать, что использовать.

13 346

13 ответов:

вы можете передать значение по умолчанию get() для ключей, которых нет в словаре:

self.val2 = kwargs.get('val2',"default value")
, Если вы планируете использовать конкретный аргумент с определенным значением по умолчанию, почему бы не использовать именованные аргументы в первую очередь?
def __init__(self, val2="default value", **kwargs):

в то время как большинство ответов говорят, что, например,

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

это "то же самое, что"

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

это не правда. В последнем случае f можно назвать f(23, 42), в то время как первый случай принимает именованные аргументы только -- нет позиционных вызовов. Часто вы хотите предоставить вызывающему максимальную гибкость, и поэтому вторая форма, как утверждают большинство ответов, предпочтительнее: но это не всегда так. Когда вы принимаете много опционных параметров которые, как правило, только несколько передаются, это может быть отличная идея (во избежание несчастных случаев и нечитаемый код на ваших сайтах вызовов!) для принудительного использования именованных аргументов -- threading.Thread пример. Первая форма - это то, как вы реализуете это в Python 2.

идиома настолько важна, что в Python 3 она теперь имеет специальный поддерживающий синтаксис: каждый аргумент после одного * на def подпись-только ключевое слово, то есть не может быть передана как позиционный аргумент, а только как именованный один. Так что в Python 3 Вы можете закодировать выше как:

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

действительно, в Python 3 Вы даже можете иметь только ключевые слова аргументы, которые не необязательно (без значения по умолчанию).

тем не менее, Python 2 все еще имеет долгие годы продуктивной жизни впереди, так что лучше не забудьте о методах и идиомах, которые позволяют реализовать в Python 2 важные дизайнерские идеи, которые непосредственно поддерживаются на языке Python 3!

Я предлагаю что-то вроде этого

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 перезапись любых дубликатов ключей.

ты

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

или

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

Если вы используете pop, то вы можете проверить, если есть какие-либо ложные значения отправлены, и принять соответствующие меры (если таковые имеются).

использовать **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" )

С **kwargs используется, когда количество аргументов неизвестно, почему бы не сделать это?

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

вот еще один подход:

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)

Я думаю, что правильный способ использовать **kwargs в Python, когда речь заходит о значениях по умолчанию, следует использовать метод словаря setdefault, как указано ниже:

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

таким образом, если пользователь передает 'val' или 'val2' в Ключевое слово args, они будут использоваться; в противном случае будут использоваться значения по умолчанию, которые были установлены.

вы могли бы сделать что-то подобное

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

на @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 список.

Если вы хотите объединить это с *args, вы должны сохранить *args и * * kwargs в конце определения.

Так:

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

@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 содержит имена аргументов, которые не встречаются в настройках по умолчанию.

еще одним простым решением для обработки неизвестных или нескольких аргументов может быть:

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()