¿Qué hacen ** (doble estrella/asterisco) y * (estrella/asterisco) para los parámetros?
¿Qué significan *args
y qué **kwargs
significan en estas definiciones de funciones?
def foo(x, y, *args):
pass
def bar(x, y, **kwargs):
pass
Consulte ¿Qué significan ** (doble estrella/asterisco) y * (estrella/asterisco) en una llamada de función? para la pregunta complementaria sobre argumentos.
Los *args
y **kwargs
son modismos comunes para permitir una cantidad arbitraria de argumentos para funciones, como se describe en la sección más detallada sobre la definición de funciones en el tutorial de Python.
Le *args
dará todos los argumentos posicionales como una tupla :
def foo(*args):
for a in args:
print(a)
foo(1)
# 1
foo(1, 2, 3)
# 1
# 2
# 3
Le **kwargs
dará todos los argumentos de palabras clave como un diccionario:
def bar(**kwargs):
for a in kwargs:
print(a, kwargs[a])
bar(name='one', age=27)
# name one
# age 27
Ambos modismos se pueden mezclar con argumentos normales para permitir un conjunto de argumentos fijos y algunos variables:
def foo(kind, *args, bar=None, **kwargs):
print(kind, args, bar, kwargs)
foo(123, 'a', 'b', apple='red')
# 123 ('a', 'b') None {'apple': 'red'}
También es posible utilizar esto al revés:
def foo(a, b, c):
print(a, b, c)
obj = {'b':10, 'c':'lee'}
foo(100, **obj)
# 100 10 lee
Otro uso del *l
modismo es descomprimir listas de argumentos al llamar a una función.
def foo(bar, lee):
print(bar, lee)
baz = [1, 2]
foo(*baz)
# 1 2
En Python 3 es posible utilizar *l
en el lado izquierdo de una tarea ( Extended Iterable Unpacking ), aunque proporciona una lista en lugar de una tupla en este contexto:
first, *rest = [1, 2, 3, 4]
# first = 1
# rest = [2, 3, 4]
Además, Python 3 agrega una nueva semántica (consulte PEP 3102 ):
def func(arg1, arg2, arg3, *, kwarg1, kwarg2):
pass
Dicha función acepta solo 3 argumentos posicionales, y todo lo que sigue *
solo se puede pasar como argumentos de palabras clave.
Nota:
Un Python dict
, utilizado semánticamente para pasar argumentos de palabras clave, está ordenado arbitrariamente. Sin embargo, en Python 3.6+, se garantiza que los argumentos de palabras clave recuerden el orden de inserción. "El orden de los elementos **kwargs
ahora corresponde al orden en que se pasaron los argumentos de las palabras clave a la función". - Novedades de Python 3.6 . De hecho, todos los dicts en CPython 3.6 recordarán el orden de inserción como un detalle de implementación, y esto se convierte en estándar en Python 3.7.
También vale la pena señalar que también puedes usar *
y **
al llamar a funciones. Este es un atajo que le permite pasar múltiples argumentos a una función directamente usando una lista/tupla o un diccionario. Por ejemplo, si tiene la siguiente función:
def foo(x,y,z):
print("x=" + str(x))
print("y=" + str(y))
print("z=" + str(z))
Puedes hacer cosas como:
>>> mylist = [1,2,3]
>>> foo(*mylist)
x=1
y=2
z=3
>>> mydict = {'x':1,'y':2,'z':3}
>>> foo(**mydict)
x=1
y=2
z=3
>>> mytuple = (1, 2, 3)
>>> foo(*mytuple)
x=1
y=2
z=3
Nota: Las claves mydict
deben tener nombres exactamente iguales a los parámetros de la función foo
. De lo contrario arrojará un TypeError
:
>>> mydict = {'x':1,'y':2,'z':3,'badnews':9}
>>> foo(**mydict)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() got an unexpected keyword argument 'badnews'
El único * significa que puede haber cualquier número de argumentos posicionales adicionales. foo()
se puede invocar como foo(1,2,3,4,5)
. En el cuerpo de foo() param2 hay una secuencia que contiene 2-5.
El doble ** significa que puede haber cualquier número de parámetros con nombre adicionales. bar()
se puede invocar como bar(1, a=2, b=3)
. En el cuerpo de bar() param2 hay un diccionario que contiene {'a':2, 'b':3 }
Con el siguiente código:
def foo(param1, *param2):
print(param1)
print(param2)
def bar(param1, **param2):
print(param1)
print(param2)
foo(1,2,3,4,5)
bar(1,a=2,b=3)
la salida es
1
(2, 3, 4, 5)
1
{'a': 2, 'b': 3}
¿ Qué hacen
**
(doble estrella) y*
(estrella) con los parámetros?
Permiten definir funciones para aceptar y que los usuarios pasen cualquier número de argumentos, posicionales ( *
) y palabras clave ( **
).
Definición de funciones
*args
permite cualquier número de argumentos posicionales opcionales (parámetros), que se asignarán a una tupla denominada args
.
**kwargs
permite cualquier número de argumentos de palabras clave opcionales (parámetros), que estarán en un diccionario llamado kwargs
.
Puede (y debe) elegir cualquier nombre apropiado, pero si la intención es que los argumentos tengan una semántica no específica args
y kwargs
sean nombres estándar.
Expansión, pasando cualquier número de argumentos.
También puede usar *args
y **kwargs
para pasar parámetros de listas (o cualquier iterable) y dicts (o cualquier mapeo), respectivamente.
La función que recibe los parámetros no tiene por qué saber que se están ampliando.
Por ejemplo, xrange de Python 2 no espera explícitamente *args
, pero dado que toma 3 números 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 solo de palabras clave
Puede tener argumentos solo de palabras clave después de *args
(por ejemplo, aquí, kwarg2
debe proporcionarse como un argumento de palabra 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 utilizar por sí solo para indicar que solo siguen argumentos de palabras clave, sin permitir argumentos posicionales ilimitados.
def foo(arg, kwarg=None, *, kwarg2=None, **kwargs):
return arg, kwarg, kwarg2, kwargs
Aquí, kwarg2
nuevamente, debe haber un argumento de palabra clave nombrado explícitamente:
>>> 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
Nuevamente, de manera más simple, aquí requerimos kwarg
que se nos proporcione por nombre, no por posición:
def bar(*, kwarg=None):
return kwarg
En este ejemplo, vemos que si intentamos pasar kwarg
posicionalmente, 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 kwarg
parámetro como argumento de palabra clave.
>>> bar(kwarg='kwarg')
'kwarg'
Demostraciones compatibles con Python 2
*args
(normalmente se dice "star-args") y **kwargs
(las estrellas pueden estar implícitas al decir "kwargs", pero sea explícito con "kwargs de doble estrella") son modismos comunes de Python para usar la notación *
y **
. Estos nombres de variables específicas no son obligatorios (por ejemplo, podría usar *foos
y **bars
), pero una desviación de la convención probablemente enfurezca a sus compañeros programadores de Python.
Normalmente los usamos cuando no sabemos qué va a recibir nuestra función o cuántos argumentos podemos pasar y, a veces, incluso cuando nombrar cada variable por separado sería muy complicado y redundante (pero este es un caso en el que normalmente es explícito). mejor que implícito).
Ejemplo 1
La siguiente función describe cómo se pueden utilizar y demuestra el comportamiento. Tenga en cuenta que el argumento nombrado b
será consumido por el segundo argumento posicional antes de:
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 en la ayuda online la firma de la función, con help(foo)
, que nos indica
foo(a, b=10, *args, **kwargs)
Llamemos a esta función confoo(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 simplemente 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, tal vez aún no estemos viendo la utilidad. Entonces 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 *args
y **kwargs
puede 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 empaquetada se puede escribir de manera mucho más concisa, ya que hemos eliminado 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 al factorizar nuestro código, lo que *args
nos **kwargs
permite hacer, reducimos líneas de código, mejoramos la legibilidad y el mantenimiento, y tenemos ubicaciones canónicas únicas para la lógica en nuestro programa. Si necesitamos cambiar alguna parte de esta estructura, tenemos un lugar para realizar cada cambio.
Primero comprendamos qué son los argumentos posicionales y los argumentos de palabras clave. A continuación se muestra un ejemplo de definición de función con argumentos posicionales.
def test(a,b,c):
print(a)
print(b)
print(c)
test(1,2,3)
#output:
1
2
3
Entonces esta es una definición de función con argumentos posicionales. También puedes llamarlo con palabras clave/argumentos con nombre:
def test(a,b,c):
print(a)
print(b)
print(c)
test(a=1,b=2,c=3)
#output:
1
2
3
Ahora estudiemos un ejemplo de definición de función con argumentos de palabras clave :
def test(a=0,b=0,c=0):
print(a)
print(b)
print(c)
print('-------------------------')
test(a=1,b=2,c=3)
#output :
1
2
3
-------------------------
También puedes llamar a esta función con argumentos posicionales:
def test(a=0,b=0,c=0):
print(a)
print(b)
print(c)
print('-------------------------')
test(1,2,3)
# output :
1
2
3
---------------------------------
Ahora conocemos las definiciones de funciones con argumentos posicionales y de palabras clave.
Ahora estudiemos el operador '*' y el operador '**'.
Tenga en cuenta que estos operadores se pueden utilizar en 2 áreas:
a) llamada a función
b) definición de función
El uso del operador '*' y del operador '**' en la llamada de función.
Vayamos directamente a un ejemplo y luego analicémoslo.
def sum(a,b): #receive args from function calls as sum(1,2) or sum(a=1,b=2)
print(a+b)
my_tuple = (1,2)
my_list = [1,2]
my_dict = {'a':1,'b':2}
# Let us unpack data structure of list or tuple or dict into arguments with help of '*' operator
sum(*my_tuple) # becomes same as sum(1,2) after unpacking my_tuple with '*'
sum(*my_list) # becomes same as sum(1,2) after unpacking my_list with '*'
sum(**my_dict) # becomes same as sum(a=1,b=2) after unpacking by '**'
# output is 3 in all three calls to sum function.
Así que recuerda
cuando el operador '*' o '**' se utiliza en una llamada de función -
El operador '*' descomprime la estructura de datos, como una lista o tupla, en los argumentos necesarios para la definición de la función.
El operador '**' descomprime un diccionario en los argumentos necesarios para la definición de la función.
Ahora estudiemos el uso del operador '*' en la definición de funciones . Ejemplo:
def sum(*args): #pack the received positional args into data structure of tuple. after applying '*' - def sum((1,2,3,4))
sum = 0
for a in args:
sum+=a
print(sum)
sum(1,2,3,4) #positional args sent to function sum
#output:
10
En la definición de función , el operador '*' empaqueta los argumentos recibidos en una tupla.
Ahora veamos un ejemplo de '**' usado en la definición de función:
def sum(**args): #pack keyword args into datastructure of dict after applying '**' - def sum({a:1,b:2,c:3,d:4})
sum=0
for k,v in args.items():
sum+=v
print(sum)
sum(a=1,b=2,c=3,d=4) #positional args sent to function sum
En la definición de función , el operador '**' empaqueta los argumentos recibidos en un diccionario.
Así que recuerda:
En una llamada de función, '*' descomprime la estructura de datos de una tupla o lista en argumentos posicionales o de palabras clave que se recibirán por definición de función.
En una llamada de función, '**' descomprime la estructura de datos del diccionario en argumentos posicionales o de palabras clave que se recibirán mediante la definición de la función.
En la definición de una función, el '*' empaqueta argumentos posicionales en una tupla.
En la definición de una función, '**' empaqueta argumentos de palabras clave en un diccionario.
Esta tabla es útil para usar *
y **
en la construcción de funciones y llamadas a funciones :
In function construction In function call
=======================================================================
| def f(*args): | def f(a, b):
*args | for arg in args: | return a + b
| print(arg) | args = (1, 2)
| f(1, 2) | f(*args)
----------|--------------------------------|---------------------------
| def f(a, b): | def f(a, b):
**kwargs | return a + b | return a + b
| def g(**kwargs): | kwargs = dict(a=1, b=2)
| return f(**kwargs) | f(**kwargs)
| g(a=1, b=2) |
-----------------------------------------------------------------------
En realidad, esto solo sirve para resumir la respuesta de Lorin Hochstein , pero la encuentro útil.
En relación: los usos de los operadores estrella/splat se han ampliado en Python 3