asterisco en función de llamada

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

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

¿Cómo es esto diferente a decir:

 uniqueCrossTabs = list(itertools.chain(uniqueCrossTabs)) 

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

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

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

En otras palabras, desea pasar cada lista en uniqueCrossTabs como un argumento para chain() , 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 la lista.

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

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

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) 

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]