6

Правильное использование **kwargs в Python

11

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

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

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

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

5 ответ(ов)

6

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

self.val2 = kwargs.get('val2', "значение по умолчанию")

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

def __init__(self, val2="значение по умолчанию", **kwargs):

Таким образом, код становится более читаемым и понятным, а также избавляет от лишних манипуляций со словарём kwargs.

3

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

def f(**kwargs):
    foo = kwargs.pop('foo')
    bar = kwargs.pop('bar')
    ...и т.д...

является "то же самым", что и

def f(foo=None, bar=None, **kwargs):
    ...и т.д...

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

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

def f(*, foo=None, bar=None, **kwargs):
    ...и т.д...

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

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

1

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

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')
# {'option1': 'new_value1', 'option2': 'default_value2', 'option3': 'new_value3'}

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

А затем используйте значения по своему усмотрению.

Функция dictionaryA.update(dictionaryB) добавляет содержимое dictionaryB в dictionaryA, перезаписывая любые дублирующиеся ключи.

0

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

Таким образом, использование **kwargs оправдано при наличии сложной логики, где просто задавать значения по умолчанию не достаточно.

0

Вы можете сделать так:

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

или так:

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

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь