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

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

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

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

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

Ответ 1

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

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

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

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

Ответ 2

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

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!

Ответ 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, перезаписывая любые повторяющиеся ключи.

Ответ 4

Вы бы сделали

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

или

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

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

Здесь другой подход:

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)

Ответ 8

Вы можете сделать что-то вроде этого

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

Ответ 9

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

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

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

Ответ 10

Следующее предложение @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.

Ответ 11

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

Итак:

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

Ответ 12

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

Ответ 13

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

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

Ответ 14

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