En Python 2.x (yo uso 2.7), ¿cuál es la forma correcta de usar los argumentos predeterminados con *args
y **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,
b
se 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 porqueb
se asigna por puesto en ese caso.Este código también funciona en Python 3. Poner el argumento predeterminado después
*args
en 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 oNone
para 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
pop
sin 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_decorator
Entonces solo haz:
@default_kwargs(defaultVar1 = defaultValue 1, ...) def foo(*args, **kwargs): # Anything in here
Por ejemplo:
@default_kwargs(a=1) def f(*args, **kwargs): print(kwargs['a']+ 1) f() # Returns 2 f(3) # Returns 4
fuente
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
*args
a 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 decorator
Resultado
@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 hacerlozip
con el final def.__code__.varnames
para hacer coincidir estos valores predeterminados con los nombres de las variables.fuente