Asterisco en llamada a función

111

Estoy usando itertools.chain para "aplanar" una lista de listas de esta manera:

uniqueCrossTabs = list(itertools.chain(*uniqueCrossTabs))

¿En qué se diferencia esto de decir:

uniqueCrossTabs = list(itertools.chain(uniqueCrossTabs))
Ramy
fuente
8
Eche un vistazo a cómo desempaquetar listas de argumentos en los documentos de Python para obtener más información.
Kai
8
también debe verificar el **operador: hace lo mismo que *pero con argumentos de palabras clave.
Sean Vieira

Respuestas:

181

* es el operador "splat": toma una lista como entrada y la expande en argumentos posicionales reales en la llamada a la función.

Entonces, si uniqueCrossTabsfue [ [ 1, 2 ], [ 3, 4 ] ], entonces itertools.chain(*uniqueCrossTabs)es lo mismo que deciritertools.chain([ 1, 2 ], [ 3, 4 ])

Obviamente, esto es diferente a pasar solo uniqueCrossTabs. En su caso, tiene una lista de listas que desea aplanar; quéitertools.chain() hace es devolver un iterador sobre la concatenación de todos los argumentos posicionales que le pasas, donde cada argumento posicional es iterable por derecho propio.

En otras palabras, desea pasar cada lista uniqueCrossTabscomo un argumento a chain(), lo que los encadenará, pero no tiene las listas en variables separadas, por lo que usa el* operador para expandir la lista de listas en varios argumentos de lista.

Como Jochen Ritzel ha señalado en los comentarios, chain.from_iterable()es más adecuado para esta operación, ya que, para empezar, asume un único iterable de iterables. Su código se convierte entonces en simplemente:

uniqueCrossTabs = list(itertools.chain.from_iterable(uniqueCrossTabs))
Cameron
fuente
9
@larsmans: Creo que el término es más popular en el mundo de Ruby, pero parece ser aceptable para Python también. Me gusta porque es divertido decirlo ;-)
Cameron
1
@larsmans: ¡Interesante! Siempre pensé que se refería a la acción de descomprimir la lista en una lista de argumentos, no al personaje en sí.
Cameron
1
Tal vez las cadenas no sean el mejor ejemplo porque no todos ven las cadenas como iterables. Por cierto: en lugar de chain(*it)escribir yo chain.from_iterable(it).
Jochen Ritzel
@Jochen: Tienes toda la razón, lo cambiaré para usar números. Además, ¡ni siquiera sabía que from_iterableexistía! Lo agregaré a mi respuesta en breve
Cameron
1
@Ramy: *es solo para expandir la lista en argumentos posicionales para una función (así que sí, muy específico). Puede hacer for l in uniqueCrossTabs:para iterar sobre ellos. Desafortunadamente, es difícil de ver *en el trabajo, ya que solo funciona cuando se pasa una lista a una función (en lugar de pasar la lista como el primer parámetro, *hace que cada elemento de la lista se pase como un parámetro separado, uno tras otro , como si se hubieran escrito separados por comas en la lista de parámetros)
Cameron
72

Divide la secuencia en argumentos separados para la llamada a la función.

>>> def foo(a, b=None, c=None):
...   print a, b, c
... 
>>> foo([1, 2, 3])
[1, 2, 3] None None
>>> foo(*[1, 2, 3])
1 2 3
>>> def bar(*a):
...   print a
... 
>>> bar([1, 2, 3])
([1, 2, 3],)
>>> bar(*[1, 2, 3])
(1, 2, 3)
Ignacio Vázquez-Abrams
fuente
explicación suciente con ejemplos. +1!
AruniRC
28

Solo una forma alternativa de explicar el concepto / usarlo.

import random

def arbitrary():
    return [x for x in range(1, random.randint(3,10))]

a, b, *rest = arbitrary()

# a = 1
# b = 2
# rest = [3,4,5]
Gelbander
fuente
3
esto es importante y no se menciona en ninguna otra parte
Gershom
1
Esta respuesta no se aplica a la pregunta específicamente, pero es una aplicación importante del asterisco (así que creo que es apropiado bajo el título bastante "nebuloso"). En la misma línea, otra aplicación importante está en las definiciones de funciones: def func(a, b, *args):consulte esta respuesta para obtener más información.
ASL