Подтвердить что ты не робот

Зачем использовать ** kwargs в python? Каковы некоторые реальные преимущества перед использованием названных аргументов?

Я исхожу из фона на статических языках. Может ли кто-нибудь объяснить (в идеале, на примере) реальный мир преимущества использования ** kwargs над именованными аргументами?

Мне кажется, что вызов функции более неоднозначен. Спасибо.

4b9b3361

Ответ 1

Примеры в реальном мире:

Декораторы - они обычно общие, поэтому вы не можете указать аргументы upfront:

def decorator(old):
    def new(*args, **kwargs):
        # ...
        return old(*args, **kwargs)
    return new

Места, где вы хотите сделать магию с неизвестным количеством аргументов ключевого слова. Django ORM делает это, например:

Model.objects.filter(foo__lt = 4, bar__iexact = 'bar')

Ответ 2

Возможно, вы захотите принять практически произвольные именованные аргументы по ряду причин - и то, что позволяет форма **kw.

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

d = dict(a=1, b=2, c=3, d=4)

если все имена должны были быть известны заранее, тогда, очевидно, такой подход просто не мог существовать, не так ли? И btw, когда это применимо, я предпочитаю этот способ сделать dict, ключи которого являются буквальными строками:

d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}

просто потому, что последний является довольно пунктуационным, и, следовательно, менее читаемым.

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

Что касается использования **kw в вызове, который позволяет собрать точный набор именованных аргументов, которые вы должны передать, каждый с соответствующими значениями, в dict, независимо от одной точки вызова, затем использовать этот dict в единственная вызывающая точка. Для сравнения:

if x: kw['x'] = x
if y: kw['y'] = y
f(**kw)

в

if x:
  if y:
    f(x=x, y=y)
  else:
    f(x=x)
else:
  if y:
    f(y=y)
  else:
    f()

Даже при наличии двух возможностей (и самого простого рода!) отсутствие **kw является aleady, делая второй вариант абсолютно несостоятельным и невыносимым - представьте себе, как это происходит, когда есть полдюжины возможностей, возможно в немного более богатом взаимодействии... без **kw, жизнь была бы абсолютным адом при таких обстоятельствах!

Ответ 3

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

class MySubclass(Superclass):
    def __init__(self, *args, **kwargs):
        self.myvalue = kwargs.pop('myvalue', None)
        super(MySubclass, self).__init__(*args, **kwargs)

Ответ 4

Существует два распространенных случая:

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

def my_wrapper(a, b, **kwargs):
    do_something_first(a, b)
    the_real_function(**kwargs)

Второе: вы готовы принять любой аргумент ключевого слова, например, для установки атрибутов для объекта:

class OpenEndedObject:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

foo = OpenEndedObject(a=1, foo='bar')
assert foo.a == 1
assert foo.foo == 'bar'

Ответ 5

**kwargs хороши, если вы заранее не знаете имя параметров. Например, конструктор dict использует их для инициализации ключей нового словаря.

dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)
In [3]: dict(one=1, two=2)
Out[3]: {'one': 1, 'two': 2}

Ответ 6

Вот пример, который я использовал в CGI Python. Я создал класс, который взял **kwargs в функцию __init__. Это позволило мне эмулировать DOM на стороне сервера с помощью классов:

document = Document()
document.add_stylesheet('style.css')
document.append(Div(H1('Imagist\ Page Title'), id = 'header'))
document.append(Div(id='body'))

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

Div(class = 'foo')

Решение заключается в доступе к базовому словарю.

Div(**{'class':'foo'})

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

Ответ 7

И вот еще один типичный пример:

MESSAGE = "Lo and behold! A message {message!r} came from {object_} with data {data!r}."

def proclaim(object_, message, data):
    print(MESSAGE.format(**locals()))

Ответ 8

Одним из примеров является python-argument-binders, который используется следующим образом:

>>> from functools import partial
>>> def f(a, b):
...     return a+b
>>> p = partial(f, 1, 2)
>>> p()
3
>>> p2 = partial(f, 1)
>>> p2(7)
8

Это из functools.partial python docs: partial является "относительно эквивалентным" этому impl:

def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(args + fargs), **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc