8

Какова цель и использование **kwargs?

1

Описание проблемы

У меня есть несколько вопросов по использованию **kwargs в Python.

  1. Я знаю, что можно использовать **kwargs при выполнении фильтрации объектов, например, с методом objects.filter, чтобы передавать аргументы.
  2. Можно ли также использовать **kwargs для указания временных интервалов, например, в timedelta(hours=time1)?
  3. Как именно это работает? Считается ли это "распаковкой" аргументов, как в примере a, b = 1, 2?

Буду благодарен за пояснения и примеры использования **kwargs в различных контекстах.

5 ответ(ов)

0

kwargs — это просто словарь, который добавляется к параметрам функции.

Словарь может содержать пары "ключ-значение", и именно это представляют собой kwargs. Давайте разберёмся с этим подробнее.

Что касается вопроса "зачем это нужно", ответ не так прост.

Например, представьте гипотетическую ситуацию, где у вас есть интерфейс, который просто вызывает другие функции для выполнения задач:

def myDo(what, where, why):
   if what == 'swim':
      doSwim(where, why)
   elif what == 'walk':
      doWalk(where, why)
   ...

Теперь вы добавляете новый метод "drive":

elif what == 'drive':
   doDrive(where, why, vehicle)

Но подождите! Теперь появляется новый параметр "vehicle", о котором вы не знали ранее. А это значит, что вам необходимо обновить сигнатуру функции myDo.

Здесь на помощь приходят kwargs — вы просто добавляете их в сигнатуру:

def myDo(what, where, why, **kwargs):
   if what == 'drive':
      doDrive(where, why, **kwargs)
   elif what == 'swim':
      doSwim(where, why, **kwargs)

Таким образом, вам не нужно будет изменять сигнатуру вашей интерфейсной функции каждый раз, когда изменяются какие-либо вызванные функции.

Это всего лишь один из примеров, когда kwargs может быть полезен.

0

Судя по вашему запросу, вы хотите получить ответ в стиле Stack Overflow на русском языке. Вот перевод на русский язык ответа на ваш вопрос:


На основе того, что хороший пример иногда лучше долгого рассуждения, я напишу две функции, использующие все возможности передачи аргументов в Python (как позиционные, так и именованные). Вам будет легко понять, что они делают:

def f(a=0, *args, **kwargs):
    print("Получено в f(a, *args, **kwargs)")
    print("=> f(a=%s, args=%s, kwargs=%s" % (a, args, kwargs))
    print("Вызов g(10, 11, 12, *args, d=13, e=14, **kwargs)")
    g(10, 11, 12, *args, d=13, e=14, **kwargs)

def g(f, g=0, *args, **kwargs):
    print("Получено в g(f, g=0, *args, **kwargs)")
    print("=> g(f=%s, g=%s, args=%s, kwargs=%s)" % (f, g, args, kwargs))

print("Вызов f(1, 2, 3, 4, b=5, c=6)")
f(1, 2, 3, 4, b=5, c=6)

Вот вывод:

Вызов f(1, 2, 3, 4, b=5, c=6)
Получено в f(a, *args, **kwargs) 
=> f(a=1, args=(2, 3, 4), kwargs={'c': 6, 'b': 5})
Вызов g(10, 11, 12, *args, d=13, e=14, **kwargs)
Получено в g(f, g=0, *args, **kwargs)
=> g(f=10, g=11, args=(12, 2, 3, 4), kwargs={'c': 6, 'b': 5, 'e': 14, 'd': 13})

Как видно из вывода, функция f принимает один обязательный аргумент и может принимать неограниченное количество дополнительных позиционных и именованных аргументов. Это позволяет передать все параметры дальше в функцию g, где они также могут быть использованы. Надеюсь, это пояснение помогло!

0

Мотив: *args и **kwargs служат заполнителями для аргументов, которые необходимо передать при вызове функции.

Используем *args для вызова функции

def args_kwargs_test(arg1, arg2, arg3):
    print("arg1:", arg1)
    print("arg2:", arg2)
    print("arg3:", arg3)

Теперь мы используем *args, чтобы вызвать заранее определенную функцию:

# args могут быть либо "списком", либо "кортежем"
>>> args = ("two", 3, 5)  
>>> args_kwargs_test(*args)

Результат:

arg1: two
arg2: 3
arg3: 5

Теперь используем **kwargs, чтобы вызвать ту же функцию:

# аргумент с ключевым словом "kwargs" должен быть словарем
>>> kwargs = {"arg3": 3, "arg2": 'two', "arg1": 5}
>>> args_kwargs_test(**kwargs)

Результат:

arg1: 5
arg2: two
arg3: 3  

Итог: *args не имеет "умности", он просто передает переданные аргументы в параметры (в порядке слева направо), в то время как **kwargs работает более разумно, размещая соответствующее значение в нужном месте.

0

kwargs в **kwargs - это всего лишь название переменной. Вы вполне можете использовать **anyVariableName.

kwargs расшифровывается как "именованные аргументы", но я считаю, что их лучше называть "именованные параметры", так как это просто параметры, передаваемые с указанием имен (я не вижу особого смысла в слове "ключевое" в термине "именованные аргументы". Обычно "ключевое" означает слова, зарезервированные языком программирования и не подлежащие использованию программистом для имен переменных. Но в случае с kwargs такой ситуации не происходит). Мы задаем имена param1 и param2 для двух передаваемых значений, как в примере func(param1="val1", param2="val2"), вместо того, чтобы передавать только значения: func(val1, val2). Поэтому я полагаю, что их следует правильно называть "произвольное число именованных аргументов", поскольку мы можем указать любое количество этих параметров (то есть аргументов), если функция имеет сигнатуру func(**kwargs).

Теперь, позвольте мне объяснить, что такое "именованные аргументы", а затем "произвольное число именованных аргументов" kwargs.

Именованные аргументы

  • именованные аргументы должны следовать за позиционными аргументами

  • порядок именованных аргументов не важен

  • Пример:

    def function1(param1, param2="arg2", param3="arg3"):
        print("\n" + str(param1) + " " + str(param2) + " " + str(param3) + "\n")
    
    function1(1)                      # 1 arg2 arg3   # 1 позиционный аргумент
    function1(param1=1)               # 1 arg2 arg3   # 1 именованный аргумент
    function1(1, param2=2)            # 1 2 arg3      # 1 позиционный и 1 именованный аргумент
    function1(param1=1, param2=2)     # 1 2 arg3      # 2 именованных аргумента       
    function1(param2=2, param1=1)     # 1 2 arg3      # 2 именованных аргумента в другом порядке
    function1(1, param3=3, param2=2)  # 1 2 3         #
    
    # function1()                      # недопустимо: отсутствует обязательный аргумент
    # function1(param2=2, 1)           # недопустимо: SyntaxError: неназванный аргумент после именованного аргумента
    # function1(1, param1=11)          # недопустимо: TypeError: function1() получил несколько значений для аргумента 'param1'
    # function1(param4=4)               # недопустимо: TypeError: function1() получил неожиданный именованный аргумент 'param4'
    

Произвольное число именованных аргументов kwargs

  • Последовательность параметров функции:

    1. позиционные параметры
    2. формальный параметр для захвата произвольного числа аргументов (с префиксом *)
    3. именованные формальные параметры
    4. формальный параметр для захвата произвольного числа именованных параметров (с префиксом **)
  • Пример:

    def function2(param1, *tupleParams, param2, param3, **dictionaryParams):
        print("param1: " + param1)
        print("param2: " + param2)
        print("param3: " + param3)
        print("пользовательские кортежные параметры", "-" * 10)
        for p in tupleParams:
            print(str(p) + ",")
        print("пользовательские именованные параметры", "-" * 10)
        for k, v in dictionaryParams.items():
            print(str(k) + ":" + str(v))
    
    function2("arg1",
              "пользовательский параметр1",
              "пользовательский параметр2",
              "пользовательский параметр3",
              param3="arg3",
              param2="arg2", 
              customNamedParam1="val1",
              customNamedParam2="val2"
              )
    
    # Вывод:
    #
    # param1: arg1
    # param2: arg2
    # param3: arg3
    # пользовательские кортежные параметры ----------
    # пользовательский параметр1,
    # пользовательский параметр2,
    # пользовательский параметр3,
    # пользовательские именованные параметры ----------
    # customNamedParam2:val2
    # customNamedParam1:val1
    

Передача кортежа и словаря для пользовательских аргументов

Чтобы завершить, замечу, что мы можем передавать

  • "формальный параметр для захвата произвольного числа аргументов" как переменную типа кортеж
  • "формальный параметр для захвата произвольного числа именованных параметров" как переменную типа словарь

Таким образом, тот же самый вызов можно сделать следующим образом:

tupleCustomArgs = ("пользовательский параметр1", "пользовательский параметр2", "пользовательский параметр3")
dictCustomNamedArgs = {"customNamedParam1": "val1", "customNamedParam2": "val2"}

function2("arg1",
      *tupleCustomArgs,    # обратите внимание на *
      param3="arg3",
      param2="arg2", 
      **dictCustomNamedArgs     # обратите внимание на **
      )

Наконец, обратите внимание на * и ** в вышеуказанных вызовах функций. Если мы опустим их, можем получить неожиданные результаты.

Опускание * в аргументах кортежа:

function2("arg1",
      tupleCustomArgs,   # опускаем *
      param3="arg3",
      param2="arg2", 
      **dictCustomNamedArgs
      )

выводит:

param1: arg1
param2: arg2
param3: arg3
пользовательские кортежные параметры ----------
('пользовательский параметр1', 'пользовательский параметр2', 'пользовательский параметр3'),
пользовательские именованные параметры ----------
customNamedParam2:val2
customNamedParam1:val1

Вышеприведенный кортеж ('пользовательский параметр1', 'пользовательский параметр2', 'пользовательский параметр3') выводится без изменения.

Опускание ** в аргументах словаря:

function2("arg1",
      *tupleCustomArgs,   
      param3="arg3",
      param2="arg2", 
      dictCustomNamedArgs   # опускаем **
      )

доставляет:

dictCustomNamedArgs
         ^
SyntaxError: неназванный аргумент после именованного аргумента
0

В дополнение, вы также можете комбинировать различные способы использования при вызове функций с аргументами kwargs:

def test(**kwargs):
    print(kwargs['a'])
    print(kwargs['b'])
    print(kwargs['c'])

args = { 'b': 2, 'c': 3 }

test(a=1, **args)

Этот код выведет следующий результат:

1
2
3

Обратите внимание, что **kwargs должен быть последним аргументом в списке параметров функции.

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