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



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



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

400   8  

8 ответов:

реальные примеры:

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

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

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

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

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

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

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

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

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)

to:

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

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

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

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

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

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

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'

**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}

вот пример, который я использовал в CGI Python. Я создал класс, который взял **kwargs до

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

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

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

один пример реализации 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.частичный 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

Comments

    Ничего не найдено.