¿Qué significan ** (doble estrella/asterisco) y * (estrella/asterisco) en una llamada de función?

Resuelto psihodelia asked hace 14 años • 5 respuestas

En código como zip(*x)o f(**k), ¿qué significan *y **respectivamente? ¿Cómo implementa Python ese comportamiento y cuáles son las implicaciones para el rendimiento?


Ver también: Expandir tuplas en argumentos . Úselo para cerrar preguntas en las que OP necesita usarlo *en un argumento y no sabe que existe. De manera similar, ¿usar la conversión de Python dict a kwargs? para el caso de utilizar **.

Consulte ¿Qué hace ** (doble estrella/asterisco) y * (estrella/asterisco) para los parámetros? para la pregunta complementaria sobre parámetros.

psihodelia avatar May 27 '10 21:05 psihodelia
Aceptado

Una sola estrella *descomprime una secuencia o colección en argumentos posicionales. Supongamos que tenemos

def add(a, b):
    return a + b

values = (1, 2)

Usando el *operador de desempaquetado, podemos escribir s = add(*values), lo que será equivalente a escribir s = add(1, 2).

La estrella doble **hace lo mismo con un diccionario, proporcionando valores para argumentos con nombre:

values = { 'a': 1, 'b': 2 }
s = add(**values) # equivalent to add(a=1, b=2)

Ambos operadores se pueden utilizar para la misma llamada de función. Por ejemplo, dado:

def sum(a, b, c, d):
    return a + b + c + d

values1 = (1, 2)
values2 = { 'c': 10, 'd': 15 }

entonces s = add(*values1, **values2)es equivalente a s = sum(1, 2, c=10, d=15).

Consulte también la sección correspondiente del tutorial en la documentación de Python.


De manera similar, *y **se puede usar para parámetros. El uso *permite que una función acepte cualquier número de argumentos posicionales, que se recopilarán en un solo parámetro:

def add(*values):
    s = 0
    for v in values:
        s = s + v
    return s

Ahora, cuando se llame a la función like s = add(1, 2, 3, 4, 5), valuesserá la tupla (1, 2, 3, 4, 5)(que, por supuesto, produce el resultado 15).

De manera similar, un parámetro marcado con **recibirá un dict:

def get_a(**values):
    return values['a']

s = get_a(a=1, b=2)      # returns 1

esto permite especificar una gran cantidad de parámetros opcionales sin tener que declararlos.

Nuevamente, ambos se pueden combinar:

def add(*values, **options):
    s = 0
    for i in values:
        s = s + i
    if "neg" in options:
        if options["neg"]:
            s = -s
    return s
        
s = add(1, 2, 3, 4, 5)            # returns 15
s = add(1, 2, 3, 4, 5, neg=True)  # returns -15
s = add(1, 2, 3, 4, 5, neg=False) # returns 15
Lasse V. Karlsen avatar May 27 '2010 14:05 Lasse V. Karlsen

En una llamada de función, la estrella única convierte una lista en argumentos separados (por ejemplo, zip(*x)es lo mismo que zip(x1, x2, x3)lo dado x=[x1,x2,x3]) y la estrella doble convierte un diccionario en argumentos de palabras clave separados (por ejemplo, f(**k)es lo mismo que f(x=my_x, y=my_y)lo dado k = {'x':my_x, 'y':my_y}).

En la definición de una función, es al revés: la estrella única convierte un número arbitrario de argumentos en una lista, y el inicio doble convierte un número arbitrario de argumentos de palabras clave en un diccionario. Por ejemplo, def foo(*x)significa "foo toma un número arbitrario de argumentos y serán accesibles a través x(es decir, si el usuario llama foo(1,2,3), xserá (1, 2, 3))" y def bar(**k)significa "bar toma un número arbitrario de argumentos de palabras clave y serán accesibles a través k(es decir, si el usuario llama bar(x=42, y=23), kserá {'x': 42, 'y': 23})".

sepp2k avatar May 27 '2010 14:05 sepp2k

Esto me parece particularmente útil para almacenar argumentos para una llamada a función.

Por ejemplo, supongamos que tengo algunas pruebas unitarias para una función 'agregar':

def add(a, b):
    return a + b

tests = { (1,4):5, (0, 0):0, (-1, 3):3 }

for test, result in tests.items():
    print('test: adding', test, '==', result, '---', add(*test) == result)

No hay otra forma de llamar a add, aparte de hacer algo como manualmente add(test[0], test[1]), lo cual es feo. Además, si hay un número variable de variables, el código podría volverse bastante feo con todas las declaraciones if que necesitaría.

Otro lugar donde esto es útil es para definir objetos Factory (objetos que crean objetos por usted). Supongamos que tiene alguna clase Factory, que crea objetos Car y los devuelve. Podrías hacerlo de modo que myFactory.make_car('red', 'bmw', '335ix')lo cree Car('red', 'bmw', '335ix')y luego lo devuelva.

def make_car(*args):
    return Car(*args)

Esto también es útil cuando quieres llamar al constructor de una superclase.

Donald Miner avatar May 27 '2010 18:05 Donald Miner

Se llama sintaxis de llamada extendida. De la documentación :

Si la sintaxis *expresión aparece en la llamada a la función, la expresión debe evaluarse como una secuencia. Los elementos de esta secuencia se tratan como si fueran argumentos posicionales adicionales; si hay argumentos posicionales x1,..., xN, y la expresión se evalúa como una secuencia y1, ..., yM, esto es equivalente a una llamada con M+N argumentos posicionales x1, ..., xN, y1, . .., yM.

y:

Si la sintaxis **expresión aparece en la llamada a la función, la expresión debe evaluarse como una asignación, cuyo contenido se trata como argumentos de palabras clave adicionales. En el caso de que una palabra clave aparezca tanto en la expresión como en un argumento de palabra clave explícito, se genera una excepción TypeError.

Mark Byers avatar May 27 '2010 14:05 Mark Byers