135

I'm using itertools.chain to "flatten" a list of lists in this fashion:

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

how is this different than saying:

uniqueCrossTabs = list(itertools.chain(uniqueCrossTabs))
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Ramy
  • 20,541
  • 41
  • 103
  • 153
  • 10
    you should also check out the `**` operator -- it does the same thing as `*` but with keyword arguments. – Sean Vieira Mar 09 '11 at 00:05
  • 9
    Take a look at [unpacking argument lists](http://docs.python.org/tutorial/controlflow.html#unpacking-argument-lists) in the Python docs for more information. – Kai Mar 09 '11 at 00:03

3 Answers3

205

* is the "splat" operator: It takes an iterable like a list as input, and expands it into actual positional arguments in the function call.

So if uniqueCrossTabs were [[1, 2], [3, 4]], then itertools.chain(*uniqueCrossTabs) is the same as saying itertools.chain([1, 2], [3, 4])

This is obviously different from passing in just uniqueCrossTabs. In your case, you have a list of lists that you wish to flatten; what itertools.chain() does is return an iterator over the concatenation of all the positional arguments you pass to it, where each positional argument is iterable in its own right.

In other words, you want to pass each list in uniqueCrossTabs as an argument to chain(), which will chain them together, but you don't have the lists in separate variables, so you use the * operator to expand the list of lists into several list arguments.

chain.from_iterable() is better-suited for this operation, as it assumes a single iterable of iterables to begin with. Your code then becomes simply:

uniqueCrossTabs = list(itertools.chain.from_iterable(uniqueCrossTabs))
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Cameron
  • 96,106
  • 25
  • 196
  • 225
83

It splits the sequence into separate arguments for the function call.

>>> 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 Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
31

Just an alternative way of explaining the concept/using it.

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
  • 1,176
  • 14
  • 20
  • 2
    This answer doesn't apply to the question specifically, but is an important application of the asterisk (so I think is appropriate under the rather "foggy" title). Along the same vein, another important application is in function definitions: `def func(a, b, *args):` See [this answer](http://stackoverflow.com/a/9539977/6748105) for more info. – ASL Oct 18 '16 at 12:28