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

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

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

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

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

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