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

6 minutos de lectura

avatar de usuario de psihodelia
psicodelia

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


Ver también: Expansión de tuplas en argumentos. Úselo para cerrar las preguntas donde OP necesita usar * en una discusión y no sabe que existe.

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

  • anexo: stackoverflow.com/questions/1141504/…

    – wds

    27 mayo 2010 a las 15:10

  • Creo que esto debería expresarse como la “* sintaxis de llamada de función”. No son operadores, aunque se volverá confuso ya que hay es a * y ** operador que no tiene nada que ver con esta sintaxis.

    – Ian Biking

    28 de mayo de 2010 a las 2:45

  • @Ian Bicking: tiene toda la razón, * y ** en la lista de argumentos son sintaxis pura (tokens).

    – P. Ortiz

    31/10/2015 a las 21:13

  • Nota: Para PEP 448: Generalizaciones adicionales de desempaquetado cosas específicas (por ejemplo, [*a, b, *c] o {**d1, **d2}), querrá leer asterisco en tupla, enumerar y establecer definiciones, doble asterisco en definición de dictado, que es específico para el uso afuera de llamadas a funciones y definiciones de funciones. para el anterior PEP 3132consulte Asignación de desempaquetado múltiple en Python cuando no conozca la longitud de la secuencia.

    – ShadowRanger

    22 de marzo de 2019 a las 20:14

Avatar de usuario de Lasse V. Karlsen
Lasse V. Karlsen

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

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

values = (1, 2)

Utilizando el * operador de desempaquetado, podemos escribir s = add(*values)que equivaldrá a escribir s = add(1, 2).

la estrella doble ** hace lo mismo para 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).

Véase también el sección relevante del tutorial en la documentación de Python.


Similarmente, * y ** puede usarse para parámetros. Usando * permite que una función acepte cualquier cantidad 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 la función se llama como s = add(1, 2, 3, 4, 5), values será 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

  • ¿Por qué necesitaría esto? ¿La función no podría simplemente iterar sobre la lista proporcionada sin que se expanda?

    – Martín Beckett

    27 de mayo de 2010 a las 14:27

  • Claro, pero entonces tendrías que llamarlo: s = sum((1, 2, 3, 4, 5)) o s = sum([1, 2, 3, 4, 5])el *values La opción hace que parezca que la llamada requiere varios argumentos, pero están empaquetados en una colección para el código de la función.

    – Lasse V. Karlsen

    27 de mayo de 2010 a las 14:29

  • Este es el beneficio real: puede escribir funciones que de otro modo no serían posibles si necesita tener un número variable de argumentos. Por ejemplo, la función printf de C, que tiene 1+n argumentos, es difícil de escribir como ejercicio para cualquier programador principiante. En Python, un principiante puede escribir def printf(string_template, *args) y continuar.

    – Ardor de Hielo

    20 de noviembre de 2013 a las 7:38

  • ¿Qué sucede si (accidentalmente tal vez: p) descomprime un diccionario con solo un * en lugar de dos? Parece que hace algo, parece que sale una tupla, pero no es tan obvio de qué se trata. (editar: ok, creo que la respuesta es que simplemente desempaqueta las claves, los valores se descartan)

    – Ben Granjero

    15 de septiembre de 2017 a las 7:47


  • El último ejemplo implica que * y ** no solo están desempacando sino también empacando. Vea esta excelente página codificacióname.com/playgrounds/500/…

    – HCChen

    17 de febrero de 2019 a las 4:06

avatar de usuario de sepp2k
sepp2k

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

En una definición de función, es al revés: la estrella única convierte un número arbitrario de argumentos en una lista, y el comienzo doble convierte un número arbitrario de argumentos de palabras clave en un diccionario. P.ej def foo(*x) significa “foo toma un número arbitrario de argumentos y serán accesibles a través de x (es decir, si el usuario llama foo(1,2,3), x será (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 de k (es decir, si el usuario llama bar(x=42, y=23), k será {'x': 42, 'y': 23})”.

Avatar de usuario de Donald Miner
Donald minero

Encuentro esto particularmente útil para almacenar argumentos para una llamada de 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 agregar, aparte de hacer manualmente algo como add(test[0], test[1]), que es feo. Además, si hay un número variable de variables, el código podría ponerse bastante feo con todas las declaraciones if que necesitarías.

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

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

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

  • Me gustan tus ejemplos. Pero creo que -1 + 3 == 2.

    – eksortso

    27 mayo 2010 a las 20:32

  • Intencionalmente puse algo allí que fallaría 🙂

    – Donald minero

    28 mayo 2010 a las 18:15

Avatar de usuario de Mark Byers
marca byers

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

Si la sintaxis *expresión aparece en la llamada de 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 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 de función, la expresión debe evaluarse como una asignación, cuyo contenido se trata como argumentos de palabra 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.

¿Ha sido útil esta solución?