Зачем использовать * * kwargs в python? Каковы некоторые реальные преимущества использования именованных аргументов?
Я родом из фона в статических языках. Может кто-нибудь объяснить (в идеале через пример) реальный мир преимущества использования * * кваргов над именованными аргументами?
Мне кажется, что это только делает вызов функции более неоднозначным. Спасибо.
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}
и вот еще один типичный пример:
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