Использование * args и ** kwargs

Так что у меня проблемы с концепцией *args и **kwargs.

На данный момент я узнал, что:

  • *args = список аргументов - как позиционные аргументы
  • **kwargs = dictionary - ключи которого становятся отдельными аргументами ключевого слова, а значения становятся значениями этих аргументов.

Я не понимаю, для какой задачи программирования это было бы полезно.

Может быть:

Я думаю вводить списки и словари в качестве аргументов функции И в то же время как подстановочный знак, чтобы я мог передать ЛЮБОЙ аргумент?

Есть ли простой пример, чтобы объяснить, как используются *args и **kwargs?

Также в учебнике, который я нашел, использовались только «*» и имя переменной.

Являются ли *args и **kwargs просто заполнителями или вы используете в коде именно *args и **kwargs?


person MacPython    schedule 03.08.2010    source источник


Ответы (11)


Используется синтаксис * и **. Имена *args и **kwargs даны только условно, но нет никаких жестких требований к их использованию.

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

>>> def print_everything(*args):
        for count, thing in enumerate(args):
...         print( '{0}. {1}'.format(count, thing))
...
>>> print_everything('apple', 'banana', 'cabbage')
0. apple
1. banana
2. cabbage

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

>>> def table_things(**kwargs):
...     for name, value in kwargs.items():
...         print( '{0} = {1}'.format(name, value))
...
>>> table_things(apple = 'fruit', cabbage = 'vegetable')
cabbage = vegetable
apple = fruit

Вы также можете использовать их вместе с именованными аргументами. Явные аргументы сначала получают значения, а затем все остальное передается в *args и **kwargs. Именованные аргументы идут первыми в списке. Например:

def table_things(titlestring, **kwargs)

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

Вы также можете использовать синтаксис * и ** при вызове функции. Например:

>>> def print_three_things(a, b, c):
...     print( 'a = {0}, b = {1}, c = {2}'.format(a,b,c))
...
>>> mylist = ['aardvark', 'baboon', 'cat']
>>> print_three_things(*mylist)
a = aardvark, b = baboon, c = cat

Как вы можете видеть, в этом случае он берет список (или кортеж) элементов и распаковывает его. Таким образом, он сопоставляет их с аргументами функции. Конечно, у вас может быть * как в определении функции, так и в ее вызове.

person Dave Webb    schedule 03.08.2010
comment
как бы вы это посмотрели в справке / документации python? - person Alex; 29.03.2014
comment
@Alex: Здесь - person Mr_and_Mrs_D; 29.07.2014
comment
Кажется невозможным расширить список, переданный в качестве позиционного аргумента в середине вызова функции, как в function_call(arg1,arg2,*expanded_list_args,arg4,arg5). Я полагаю, что за расширенным списком могут следовать только ключевые аргументы. Есть ли способ обойти это? - person mlh3789; 25.09.2014
comment
@ mlh3789 да, и это работает только с python3. Но что действительно немного странно: это вроде работает с присваиваниями: a, b, *c, d, e = 1, 2, 3, 4, 5, 6 присваивает c [3, 4]. Немного сбивает с толку - person Christian Tismer; 06.10.2014
comment
В приведенном выше примере mylist похоже на an array в JS. Наш обычный метод print_three_things принимает 3 аргумента. При передаче в print_three_things (*mylist) аннотация * более или менее похожа на spreading operator в ES6. Сообщите мне, в порядке ли я? Спасибо - person Pristine Kallio; 22.06.2017
comment
Спасибо, что указали, что вы можете отправлять кортежи / списки в * args .. супер полезно - person alpha_989; 01.01.2018
comment
You can also use both in the same function definition but *args must occur before **kwargs. Почему? - person piepi; 24.04.2018
comment
Есть ли разница в производительности ч / б у них обоих? - person Genius; 04.05.2021

Одно место, где использование *args и **kwargs весьма полезно, - это создание подклассов.

class Foo(object):
    def __init__(self, value1, value2):
        # do something with the values
        print value1, value2

class MyFoo(Foo):
    def __init__(self, *args, **kwargs):
        # do something else, don't care about the args
        print 'myfoo'
        super(MyFoo, self).__init__(*args, **kwargs)

Таким образом, вы можете расширить поведение класса Foo, не зная о Foo слишком много. Это может быть очень удобно, если вы программируете API, который может измениться. MyFoo просто передает все аргументы классу Foo.

person Mark van Lent    schedule 03.08.2010
comment
Этот ответ действительно имеет смысл только в том случае, если вы уже понимаете * и **. - person Scott Tesler; 25.02.2013
comment
Я не понимаю. Если ваш API изменится, вам все равно придется изменить все места, где вы создаете экземпляры дочерних классов. И если вы меняете все эти места, тогда вы можете также исправить подпись дочернего класса, а не взламывать вместе с args и kwargs без всякой причины. Ваши рассуждения о том, что для этого не требуется знание Foo, бессмысленны, потому что, как только заданная сигнатура конструктора Foo изменится, все ваши вызовы экземпляров MyFoo также должны будут измениться. Это требует знания Foo и параметров, необходимых его конструктору. - person Zoran Pavlovic; 28.05.2013
comment
@ZoranPavlovic Примером, где это можно было бы использовать, является ситуация, когда вы предоставляете надстройку для существующего продукта и хотите переопределить / расширить некоторые функции. Используя * args и ** kwargs, это дополнение будет продолжать работать, если основной продукт изменится. Однако создание экземпляра MyFoo происходит вне надстройки. Имеет ли это (больший) смысл? (Сказав это: декоратор - лучший пример того, когда вы можете использовать * args и ** kwargs.) - person Mark van Lent; 29.05.2013
comment
как вы расширите это для конкретных аргументов подкласса? - person Hanan Shteingart; 12.08.2014
comment
@HananShteingart Например, вот так: def __init __ (self, foo, * args, ** kwargs) - person Mark van Lent; 19.08.2014
comment
В этом конкретном примере нет гарантии, что value1 и value2 будут пройдены. Кроме того, если передано более 2 аргументов, возникает исключение. В заключение, это не лучший пример переадресации аргументов. - person kavinyao; 24.06.2015
comment
... а затем можно создавать такие объекты, как f = MyFoo((1,2,3),{'third':4,'fifth':5}). Здесь первый кортеж (1,2,3) будет аргументом для args, а словарь позже перейдет в kwargs. Важно отметить, что такой вызов, как f = MyFoo(1,2,3,4,5), становится неправильным, поскольку интерпретатор Python не может определить, какой аргумент должен быть частью кортежа, а какой должен принадлежать kwargs (словарю). Таким образом, при совместном использовании *args и **kwargs вам в значительной степени необходимо сначала сформировать аргументы, которые необходимо передать. Если бы там не было **kwargs, второй звонок был бы прекрасен. - person ViFI; 26.07.2016
comment
Это очень ограниченный вариант использования, и большинство людей не будут использовать этот шаблон в описанном вами сценарии. Они воспользуются этим как взломом - я видел это, и это может причинить много горя. По сути, вы даже обходите ограниченные проверки, которые предоставляет интерпретатор Python. - person s5s; 12.01.2017
comment
@kavinyao: вы явно не знаете Python и не тестировали то, что написали. value2 идет первым элементом в args, поэтому не исключение. - person Marco Sulla; 24.12.2019
comment
@VIFI: то же самое ... Диктовка будет вторым элементом args. Чтобы они были kwargs, вы должны написать MyFoo(1, 2, 3, third=4, fifth=5). И MyFoo(1,2,3,4,5) создаст args=(1,2,3,4,5) Пожалуйста, вы оба, прочтите документацию и проверьте, прежде чем произносить совершенно неправильные утверждения. - person Marco Sulla; 24.12.2019
comment
@ZoranPavlovic: см. Этот пример: stackoverflow.com/a/34393976/1763602 Как видите, мне не нужно знать фирму dict конструктора для создания моей собственной карты. Если dict API изменится, мой MultiDict продолжит работать. Конечно, вы должны изменить свой код, но и те, кто использовал dict, должны его изменить. Но код MultiDict менять не нужно. - person Marco Sulla; 24.12.2019

Вот пример, в котором используются 3 разных типа параметров.

def func(required_arg, *args, **kwargs):
    # required_arg is a positional-only parameter.
    print required_arg

    # args is a tuple of positional arguments,
    # because the parameter name has * prepended.
    if args: # If args is not empty.
        print args

    # kwargs is a dictionary of keyword arguments,
    # because the parameter name has ** prepended.
    if kwargs: # If kwargs is not empty.
        print kwargs

>>> func()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() takes at least 1 argument (0 given)

>>> func("required argument")
required argument

>>> func("required argument", 1, 2, '3')
required argument
(1, 2, '3')

>>> func("required argument", 1, 2, '3', keyword1=4, keyword2="foo")
required argument
(1, 2, '3')
{'keyword2': 'foo', 'keyword1': 4}
person Kit    schedule 03.08.2010

Вот одно из моих любимых мест для использования синтаксиса **, как в последнем примере Дэйва Уэбба:

mynum = 1000
mystr = 'Hello World!'
print("{mystr} New-style formatting is {mynum}x more fun!".format(**locals()))

Я не уверен, что это ужасно быстро по сравнению с использованием самих имен, но набирать его намного проще!

person Wayne Werner    schedule 03.08.2010
comment
эй, этот парень изобрел ф-струны Python 3.6 до того, как это стало круто - person cat; 23.05.2017
comment
@cat объясните пожалуйста? о чем именно вы говорите? разве метод format еще не существовал даже в Python 2? - person dabadaba; 28.09.2017
comment
@dabadaba f-строки, а не строки формата (например, f'{mystr} New-style formatting is {mynum}x more fun!') - person Wayne Werner; 28.09.2017
comment
@dabadaba поздравляет с одним из сегодняшних счастливых 10k - person Wayne Werner; 29.09.2017
comment
@dabadaba см. python.org/dev/peps/pep-0498. - person cat; 29.09.2017
comment
Убедитесь, что вы действительно не используете строку от пользователя, иначе это наверняка создаст большую дыру в безопасности. - person hegez; 24.05.2018
comment
@hegez в каком контексте? Если вы используете строковое форматирование для SQL-запросов, то вы либо глупы, либо еще не узнали о параметризованных запросах. - person Wayne Werner; 25.05.2018
comment
@WayneWerner никто не говорил о запросах SQL (хотя я понимаю, что они играют большую роль примера в этой области). Думаю, я подумал о том, чтобы позволить пользователю хотя бы частично установить строку, которая будет отформатирована. Очень упрощенный случай: string = '{locale_username}: ' + input(); string.format(**locals()). Кроме того, программисты должны .format() строку только один раз. - person hegez; 26.05.2018

Один случай, когда * args и ** kwargs полезны, - это при написании функций-оберток (таких как декораторы), которые должны иметь возможность принимать произвольные аргументы для передачи в оборачиваемую функцию. Например, простой декоратор, который печатает аргументы и возвращаемое значение оборачиваемой функции:

def mydecorator( f ):
   @functools.wraps( f )
   def wrapper( *args, **kwargs ):
      print "Calling f", args, kwargs
      v = f( *args, **kwargs )
      print "f returned", v
      return v
   return wrapper
person jchl    schedule 03.08.2010
comment
Зачем в этом случае нужны другие аргументы и kwargs ?? Использование только одного из двух не даст того же? - person Daniel González Fernández; 01.01.2021

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

def sumFunction(*args):
  result = 0
  for x in args:
    result += x
  return result

и используйте его как sumFunction (3,4,6,3,6,8,9).

** kwargs имеет другую функцию. С помощью ** kwargs вы можете передать функции произвольные аргументы ключевого слова и получить к ним доступ как к словарю.

def someFunction(**kwargs):
  if 'text' in kwargs:
    print kwargs['text']

Вызов someFunction (text = "foo") напечатает foo.

person Steven Mohr    schedule 03.08.2010

Только представьте, что у вас есть функция, но вы не хотите ограничивать количество требуемых параметров. Пример:

>>> import operator
>>> def multiply(*args):
...  return reduce(operator.mul, args)

Затем вы используете эту функцию как:

>>> multiply(1,2,3)
6

or

>>> numbers = [1,2,3]
>>> multiply(*numbers)
6
person Felix Kling    schedule 03.08.2010

Имена *args и **kwargs или **kw являются чисто условными. Нам легче читать код друг друга

Одно место, где это удобно, - это использование модуля структуры

struct.unpack() возвращает кортеж, тогда как struct.pack() использует переменное количество аргументов. При манипулировании данными удобно иметь возможность передать кортеж в struck.pack(), например.

tuple_of_data = struct.unpack(format_str, data)
... manipulate the data
new_data = struct.pack(format_str, *tuple_of_data)

без этой способности вы были бы вынуждены написать

new_data = struct.pack(format_str, tuple_of_data[0], tuple_of_data[1], tuple_of_data[2],...)

что также означает, что если изменится format_str и изменится размер кортежа, мне придется вернуться и отредактировать эту действительно длинную строку

person John La Rooy    schedule 03.08.2010

Обратите внимание, что * args / ** kwargs является частью синтаксиса вызова функции, а не оператором. Это имеет особый побочный эффект, с которым я столкнулся, а именно то, что вы не можете использовать расширение * args с оператором печати, поскольку print не является функцией.

Это кажется разумным:

def myprint(*args):
    print *args

К сожалению, он не компилируется (синтаксическая ошибка).

Это компилирует:

def myprint(*args):
    print args

Но выводит аргументы как кортеж, а это не то, что нам нужно.

Это решение, на котором я остановился:

def myprint(*args):
    for arg in args:
        print arg,
    print
person yoyo    schedule 10.11.2010
comment
Конечно всегда есть from __future__ import print_function :) - person pfctdayelise; 21.11.2011
comment
print - это функция Python3 :) docs.python.org/3.0/whatsnew/3.0. html - person sk8asd123; 30.08.2013

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

def foo(bar=2, baz=5):
    print bar, baz

def proxy(x, *args, **kwargs): # reqire parameter x and accept any number of additional arguments
    print x
    foo(*args, **kwargs) # applies the "non-x" parameter to foo

proxy(23, 5, baz='foo') # calls foo with bar=5 and baz=foo
proxy(6)# calls foo with its default arguments
proxy(7, bar='asdas') # calls foo with bar='asdas' and leave baz default argument

Но поскольку эти параметры скрывают фактические имена параметров, их лучше избегать.

person Rudi    schedule 03.08.2010

Вы можете взглянуть на документацию python (docs.python.org в FAQ), но более конкретно для хорошего объяснения таинственные пропущенные аргументы и мистер kwargs (любезно предоставлено archive.org) ( исходная неработающая ссылка: здесь).

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

myfunction(myarg=1)
person Yoni H    schedule 03.08.2010
comment
Еще один хороший учебник: freepythontips.wordpress.com / 2013/08/04 / - person sk8asd123; 30.08.2013
comment
«Вот ссылка, а также то, что сказал этот человек» - бесполезный ответ. Единственное, о чем этот ответ фактически говорит сам по себе, - это подразумевать, что **kwargs требуется для использования именованных аргументов, что неверно. Это необходимо только для обработки произвольных именованных аргументов. - person underscore_d; 10.06.2017