Аргументы по умолчанию с *args и * * kwargs


на Python 2.x (Я использую 2.7), который является правильным способом использовать аргументы по умолчанию с *args и **kwargs?
Я нашел вопрос по SO, связанный с этой темой, но это для Python 3:
вызов функции Python с помощью *args,* * kwargs и необязательных / стандартных аргументов

там, они говорят, что этот метод работает:

def func(arg1, arg2, *args, opt_arg='def_val', **kwargs):
    #...

в 2.7, это приводит к SyntaxError. Есть ли рекомендуемый способ определить такой функции?
Я получил это работает, но я думаю, что есть лучше решение.

def func(arg1, arg2, *args, **kwargs):
    opt_arg ='def_val'
    if kwargs.__contains__('opt_arg'):
        opt_arg = kwargs['opt_arg']
    #...
5 60

5 ответов:

просто поставьте аргументы по умолчанию перед *args:

def foo(a, b=3, *args, **kwargs):

теперь b будет явно установлен, если вы передадите его в качестве ключевого аргумента или второго позиционного аргумента.

примеры:

foo(x) # a=x, b=3, args=(), kwargs={}
foo(x, y) # a=x, b=y, args=(), kwargs={}
foo(x, b=y) # a=x, b=y, args=(), kwargs={}
foo(x, y, z, k) # a=x, b=y, args=(z, k), kwargs={}
foo(x, c=y, d=k) # a=x, b=3, args=(), kwargs={'c': y, 'd': k}
foo(x, c=y, b=z, d=k) # a=x, b=z, args=(), kwargs={'c': y, 'd': k}

обратите внимание, что, в частности, foo(x, y, b=z) не работает, потому что b присваивается по должности в этом случае.


этот код работает и в Python 3. Ставим по умолчанию arg после*args в Python 3 делает его a "ключевое слово-только" аргумент, который может только указывается по имени, а не по должности. Если вам нужен аргумент только для ключевых слов в Python 2, Вы можете использовать решение @mgilson.

синтаксис в другом вопросе-python3.только x и задает аргументы только для ключевых слов. Это не работает на python2.x.

для python2.х, я бы pop это из kwargs:

def func(arg1, arg2, *args, **kwargs):
    opt_arg = kwargs.pop('opt_arg', 'def_val')

еще один способ справиться с Python 2.x:

def foo(*args, **kwargs):
    if < kwarg-name > not in kwargs.keys():
        kwargs[< kwarg-name >] = < kwarg-name-default-value >
    return bar(*args, **kwargs)

это обрабатывает передачу произвольных *args к основному вызову в отличие от ответа @nneonneo.

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

>>> def func(arg1, arg2, *args, **kwargs):
...     kwargs_with_defaults = dict({'opt_arg': 'def_val', 'opt_arg2': 'default2'}, **kwargs)
...     #...
...     return arg1, arg2, args, kwargs_with_defaults

>>> func('a1', 'a2', 'a3', 'a5', x='foo', y='bar')
('a1', 'a2', ('a3', 'a5'), {'opt_arg2': 'default2', 'opt_arg': 'def_val', 'y': 'bar', 'x': 'foo'})

>>> func('a1', 'a2', 'a3', 'a5', opt_arg='explicit_value', x='foo', y='bar')
('a1', 'a2', ('a3', 'a5'), {'opt_arg2': 'default2', 'opt_arg': 'explicit_value', 'y': 'bar', 'x': 'foo'})

вы также можете использовать декоратор такой:

import functools
def default_kwargs(**defaultKwargs):
    def actual_decorator(fn):
        @functools.wraps(fn)
        def g(*args, **kwargs):
            defaultKwargs.update(kwargs)
            return fn(*args, **defaultKwargs)
        return g
    return actual_decorator

тогда просто сделайте:

@default_kwargs(defaultVar1 = defaultValue 1, ...)
def foo(*args, **kwargs):
    # Anything in here

например:

@default_kwargs(a=1)
def f(*args, **kwargs):
    print(kwargs['a']+ 1)

f() # Returns 2
f(3) # Returns 4