¿Qué hacen **(estrella doble) y *(estrella) para los parámetros?
Permiten que las funciones se definan para aceptar y que los usuarios pasen cualquier número de argumentos, posicionales ( *) y palabras clave ( **).
Definiendo funciones
*argspermite cualquier número de argumentos posicionales opcionales (parámetros), que se asignarán a una tupla llamada args.
**kwargspermite cualquier número de argumentos de palabra clave opcionales (parámetros), que estarán en un dict con nombre kwargs.
Puede (y debe) elegir cualquier nombre apropiado, pero si la intención es que los argumentos sean de semántica no específica argsy que kwargssean nombres estándar.
Expansión, pasando cualquier número de argumentos
También puede usar *argsy **kwargspasar parámetros de listas (o cualquier iterable) y dictados (o cualquier mapeo), respectivamente.
La función que recibe los parámetros no tiene que saber que se están expandiendo.
Por ejemplo, xrange de Python 2 no espera explícitamente *args, pero dado que toma 3 enteros como argumentos:
>>> x = xrange(3) # create our *args - an iterable of 3 integers
>>> xrange(*x) # expand here
xrange(0, 2, 2)
Como otro ejemplo, podemos usar la expansión dict en str.format:
>>> foo = 'FOO'
>>> bar = 'BAR'
>>> 'this is foo, {foo} and bar, {bar}'.format(**locals())
'this is foo, FOO and bar, BAR'
Nuevo en Python 3: definición de funciones con argumentos de solo palabras clave
Puede tener argumentos de solo palabras clave después de *args, por ejemplo, aquí, kwarg2debe darse como argumento de palabras clave, no posicionalmente:
def foo(arg, kwarg=None, *args, kwarg2=None, **kwargs):
return arg, kwarg, args, kwarg2, kwargs
Uso:
>>> foo(1,2,3,4,5,kwarg2='kwarg2', bar='bar', baz='baz')
(1, 2, (3, 4, 5), 'kwarg2', {'bar': 'bar', 'baz': 'baz'})
Además, *se puede usar solo para indicar que siguen los argumentos de palabras clave, sin permitir argumentos posicionales ilimitados.
def foo(arg, kwarg=None, *, kwarg2=None, **kwargs):
return arg, kwarg, kwarg2, kwargs
Aquí, kwarg2nuevamente debe haber un argumento de palabra clave explícitamente nombrado:
>>> foo(1,2,kwarg2='kwarg2', foo='foo', bar='bar')
(1, 2, 'kwarg2', {'foo': 'foo', 'bar': 'bar'})
Y ya no podemos aceptar argumentos posicionales ilimitados porque no tenemos *args*:
>>> foo(1,2,3,4,5, kwarg2='kwarg2', foo='foo', bar='bar')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes from 1 to 2 positional arguments
but 5 positional arguments (and 1 keyword-only argument) were given
De nuevo, más simplemente, aquí necesitamos kwargque se nos dé por nombre, no por posición:
def bar(*, kwarg=None):
return kwarg
En este ejemplo, vemos que si intentamos pasar kwargposicionalmente, obtenemos un error:
>>> bar('kwarg')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: bar() takes 0 positional arguments but 1 was given
Debemos pasar explícitamente el kwargparámetro como argumento de palabra clave.
>>> bar(kwarg='kwarg')
'kwarg'
Demos compatibles con Python 2
*args(normalmente se dice "star-args") y **kwargs(las estrellas pueden estar implícitas diciendo "kwargs", pero sea explícito con "double-star kwargs") son expresiones idiomáticas comunes de Python para usar la notación *y **. Estos nombres de variables específicos no son obligatorios (por ejemplo, podría usar *foosy **bars), pero es probable que una desviación de la convención enfurezca a sus compañeros codificadores de Python.
Por lo general, los usamos cuando no sabemos qué va a recibir nuestra función o cuántos argumentos podemos estar pasando, y a veces incluso cuando nombrar cada variable por separado se volvería muy desordenada y redundante (pero este es un caso donde generalmente es explícito mejor que implícito).
Ejemplo 1
La siguiente función describe cómo se pueden usar y demuestra el comportamiento. Tenga en cuenta que el bargumento nombrado será consumido por el segundo argumento posicional antes:
def foo(a, b=10, *args, **kwargs):
'''
this function takes required argument a, not required keyword argument b
and any number of unknown positional arguments and keyword arguments after
'''
print('a is a required argument, and its value is {0}'.format(a))
print('b not required, its default value is 10, actual value: {0}'.format(b))
# we can inspect the unknown arguments we were passed:
# - args:
print('args is of type {0} and length {1}'.format(type(args), len(args)))
for arg in args:
print('unknown arg: {0}'.format(arg))
# - kwargs:
print('kwargs is of type {0} and length {1}'.format(type(kwargs),
len(kwargs)))
for kw, arg in kwargs.items():
print('unknown kwarg - kw: {0}, arg: {1}'.format(kw, arg))
# But we don't have to know anything about them
# to pass them to other functions.
print('Args or kwargs can be passed without knowing what they are.')
# max can take two or more positional args: max(a, b, c...)
print('e.g. max(a, b, *args) \n{0}'.format(
max(a, b, *args)))
kweg = 'dict({0})'.format( # named args same as unknown kwargs
', '.join('{k}={v}'.format(k=k, v=v)
for k, v in sorted(kwargs.items())))
print('e.g. dict(**kwargs) (same as {kweg}) returns: \n{0}'.format(
dict(**kwargs), kweg=kweg))
Podemos consultar la ayuda en línea para la firma de la función, con help(foo), que nos dice
foo(a, b=10, *args, **kwargs)
Llamemos a esta función con foo(1, 2, 3, 4, e=5, f=6, g=7)
que imprime:
a is a required argument, and its value is 1
b not required, its default value is 10, actual value: 2
args is of type <type 'tuple'> and length 2
unknown arg: 3
unknown arg: 4
kwargs is of type <type 'dict'> and length 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: g, arg: 7
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args)
4
e.g. dict(**kwargs) (same as dict(e=5, f=6, g=7)) returns:
{'e': 5, 'g': 7, 'f': 6}
Ejemplo 2
También podemos llamarlo usando otra función, en la que solo proporcionamos a:
def bar(a):
b, c, d, e, f = 2, 3, 4, 5, 6
# dumping every local variable into foo as a keyword argument
# by expanding the locals dict:
foo(**locals())
bar(100) huellas dactilares:
a is a required argument, and its value is 100
b not required, its default value is 10, actual value: 2
args is of type <type 'tuple'> and length 0
kwargs is of type <type 'dict'> and length 4
unknown kwarg - kw: c, arg: 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: d, arg: 4
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args)
100
e.g. dict(**kwargs) (same as dict(c=3, d=4, e=5, f=6)) returns:
{'c': 3, 'e': 5, 'd': 4, 'f': 6}
Ejemplo 3: uso práctico en decoradores
Bien, quizás aún no estemos viendo la utilidad. Imagine que tiene varias funciones con código redundante antes y / o después del código diferenciador. Las siguientes funciones nombradas son solo pseudocódigo con fines ilustrativos.
def foo(a, b, c, d=0, e=100):
# imagine this is much more code than a simple function call
preprocess()
differentiating_process_foo(a,b,c,d,e)
# imagine this is much more code than a simple function call
postprocess()
def bar(a, b, c=None, d=0, e=100, f=None):
preprocess()
differentiating_process_bar(a,b,c,d,e,f)
postprocess()
def baz(a, b, c, d, e, f):
... and so on
Es posible que podamos manejar esto de manera diferente, pero ciertamente podemos extraer la redundancia con un decorador, por lo que nuestro siguiente ejemplo demuestra cómo *argsy **kwargspuede ser muy útil:
def decorator(function):
'''function to wrap other functions with a pre- and postprocess'''
@functools.wraps(function) # applies module, name, and docstring to wrapper
def wrapper(*args, **kwargs):
# again, imagine this is complicated, but we only write it once!
preprocess()
function(*args, **kwargs)
postprocess()
return wrapper
Y ahora cada función envuelta se puede escribir de manera mucho más sucinta, ya que tenemos en cuenta la redundancia:
@decorator
def foo(a, b, c, d=0, e=100):
differentiating_process_foo(a,b,c,d,e)
@decorator
def bar(a, b, c=None, d=0, e=100, f=None):
differentiating_process_bar(a,b,c,d,e,f)
@decorator
def baz(a, b, c=None, d=0, e=100, f=None, g=None):
differentiating_process_baz(a,b,c,d,e,f, g)
@decorator
def quux(a, b, c=None, d=0, e=100, f=None, g=None, h=None):
differentiating_process_quux(a,b,c,d,e,f,g,h)
Y por factorización de nuestro código, el cual *args, y **kwargsnos permite hacer, podemos reducir líneas de código, mejorar la legibilidad y facilidad de mantenimiento, y tienen lugares únicos canónicas para la lógica en nuestro programa. Si necesitamos cambiar alguna parte de esta estructura, tenemos un lugar en el que hacer cada cambio.