В 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']
#...
python
python-2.7
Codeforester
источник
источник
__contains__
. Всегда используйтеin
:'opt_arg' in kwargs
. (Еще лучше:kwargs.get('opt_arg', 'def_val')
как в ответе Мильсона).Ответы:
Просто поместите аргументы по умолчанию перед
*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. Установка аргумента по умолчанию после
*args
в Python 3 делает его аргументом «только для ключевых слов», который можно указывать только по имени, а не по позиции. Если вам нужен аргумент только с ключевыми словами в Python 2, вы можете использовать решение @mgilson.источник
*args
. Ваш способ верен, если вам нужен аргумент, состоящий только из ключевых слов.*args
, нет. Вот почему эта функциональность была добавлена в Python 3. Обычно аргументы по умолчанию в Python 2 указываются как нечто очевидное, например 0 илиNone
около того, чтобы их можно было явно передать.Синтаксис в другом вопросе - только python3.x и указывает аргументы только по ключевым словам. Это не работает на python2.x.
Для python2.x я бы
pop
из kwargs:def func(arg1, arg2, *args, **kwargs): opt_arg = kwargs.pop('opt_arg', 'def_val')
источник
Вы также можете использовать такой декоратор:
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
источник
Придерживаясь вашего подхода к решению, пытаясь сделать его более общим и компактным, я бы предложил рассмотреть что-то вроде этого:
>>> 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'})
источник
Подобный подход к @yaccob, но ясный и лаконичный:
В Python 3.5 или выше:
def foo(a, b=3, *args, **kwargs): defaultKwargs = { 'c': 10, 'd': 12 } kwargs = { **defaultKwargs, **kwargs } print(a, b, args, kwargs) # Do something foo(1) # 1 3 () {'c': 10, 'd': 12} foo(1, d=5) # 1 3 () {'c': 10, 'd': 5} foo(1, 2, 4, d=5) # 1 2 (4,) {'c': 10, 'd': 5}
Примечание: вы можете использовать в Python 2
В Python 3.5
В Python 3.9
kwargs = defaultKwargs | kwargs # NOTE: 3.9+ ONLY
источник
Другой способ работы с 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.источник
'opt_arg'
вместо< kwarg-name >
и'def_val'
вместо< kwarg-name-default-value >
Этот ответ является продолжением того, что предложил Даниэль Америко .
Этот декоратор назначает значения kwarg по умолчанию, если они не определены строго .
from functools import wraps def force_kwargs(**defaultKwargs): def decorator(f): @wraps(f) def g(*args, **kwargs): new_args = {} new_kwargs = defaultKwargs varnames = f.__code__.co_varnames new_kwargs.update(kwargs) for k, v in defaultKwargs.items(): if k in varnames: i = varnames.index(k) new_args[(i, k)] = new_kwargs.pop(k) # Insert new_args into the correct position of the args. full_args = list(args) for i, k in sorted(new_args.keys()): if i <= len(full_args): full_args.insert(i, new_args.pop((i, k))) else: break # re-insert the value as a key-value pair for (i, k), val in new_args.items(): new_kwargs[k] = val return f(*tuple(full_args), **new_kwargs) return g return decorator
Результат
@force_kwargs(c=7) def f(a, b='B', c='C', d='D', *args, **kw): return a, b, c, d, args, kw # a b c d args kwargs f('r') # 'r', 'B', 7, 'D', (), {} f(1,2,3) # 1, 2, 7, 3, (), {} f(1, 2, 3, b=3, c=9, f='F') # 1, 3, 9, 2, (3,), {'f': 'F'}
Вариант
Если вы хотите использовать значения по умолчанию, как написано в определении функции, вы можете получить доступ к значениям аргументов по умолчанию, используя
f.func_defaults
, в котором перечислены значения по умолчанию. Вам нужно будет указатьzip
их в конце,f.__code__.varnames
чтобы эти значения по умолчанию совпадали с именами переменных.источник