En Python 2.x (yo uso 2.7), ¿cuál es la forma correcta de usar los argumentos predeterminados con *argsy **kwargs?
Encontré una pregunta sobre SO relacionada con este tema, pero esa es para Python 3 :
llamar a una función de Python con * args, ** kwargs y argumentos opcionales / predeterminados
Allí, dicen que este método funciona:
def func(arg1, arg2, *args, opt_arg='def_val', **kwargs):
#...
En 2.7, resulta en un SyntaxError. ¿Existe alguna forma recomendada de definir dicha función?
Lo hice funcionar de esta manera, pero supongo que hay una mejor solución.
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
fuente
fuente

__contains__. Utilice siemprein:'opt_arg' in kwargs. (Aún mejor:kwargs.get('opt_arg', 'def_val')como en la respuesta de mgilson).Respuestas:
Simplemente coloque los argumentos predeterminados antes de
*args:def foo(a, b=3, *args, **kwargs):Ahora,
bse establecerá explícitamente si lo pasa como un argumento de palabra clave o el segundo argumento posicional.Ejemplos:
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}Tenga en cuenta que, en particular,
foo(x, y, b=z)no funciona porquebse asigna por puesto en ese caso.Este código también funciona en Python 3. Poner el argumento predeterminado después
*argsen Python 3 lo convierte en un argumento "solo de palabra clave" que solo se puede especificar por nombre, no por posición. Si desea un argumento de solo palabras clave en Python 2, puede usar la solución de @ mgilson.fuente
*args. Tu camino es correcto si quieres un argumento de solo palabras clave.*args, no. Esta es la razón por la que se agregó la funcionalidad a Python 3. Normalmente, los argumentos predeterminados en Python 2 se especifican como algo obvio como 0 oNonepara que puedan pasarse explícitamente.La sintaxis en la otra pregunta es solo python3.x y especifica solo argumentos de palabras clave. No funciona en python2.x.
Para python2.x, lo haría
popsin kwargs:def func(arg1, arg2, *args, **kwargs): opt_arg = kwargs.pop('opt_arg', 'def_val')fuente
También podrías usar un decorador como este:
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_decoratorEntonces solo haz:
@default_kwargs(defaultVar1 = defaultValue 1, ...) def foo(*args, **kwargs): # Anything in herePor ejemplo:
@default_kwargs(a=1) def f(*args, **kwargs): print(kwargs['a']+ 1) f() # Returns 2 f(3) # Returns 4fuente
Siguiendo bastante cerca de su enfoque de solución mientras intenta hacerlo más genérico y más compacto, sugeriría considerar algo como esto:
>>> 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'})fuente
Otra forma de manejar 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)Esto maneja el paso arbitrario
*argsa la llamada subyacente a diferencia de la respuesta de @ nneonneo.fuente
'opt_arg'lugar de< kwarg-name >y en'def_val'lugar de< kwarg-name-default-value >Esta respuesta es una extensión de lo que sugirió Daniel Américo .
Este decorador asigna valores kwarg predeterminados si no están estrictamente definidos.
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 decoratorResultado
@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'}Variante
Si desea utilizar los valores predeterminados tal como están escritos en la definición de la función, puede acceder a los valores predeterminados del argumento utilizando
f.func_defaults, que enumera los valores predeterminados. Tendría que hacerlozipcon el final def.__code__.varnamespara hacer coincidir estos valores predeterminados con los nombres de las variables.fuente