J'utilise itertools.chain pour "aplatir" une liste de listes de cette façon:
uniqueCrossTabs = list(itertools.chain(*uniqueCrossTabs))
en quoi est-ce différent de dire:
uniqueCrossTabs = list(itertools.chain(uniqueCrossTabs))
*
Est l'opérateur "splat": il prend une liste en entrée et la développe en arguments positionnels réels dans l'appel de fonction.
Donc, si uniqueCrossTabs
était [ [ 1, 2 ], [ 3, 4 ] ]
, Alors itertools.chain(*uniqueCrossTabs)
revient à dire itertools.chain([ 1, 2 ], [ 3, 4 ])
C'est évidemment différent de passer juste uniqueCrossTabs
. Dans votre cas, vous avez une liste de listes que vous souhaitez aplatir; ce que itertools.chain()
fait, c'est retourner un itérateur sur la concaténation de tous les arguments positionnels que vous lui passez, où chaque argument positionnel est itérable à part entière.
En d'autres termes, vous voulez passer chaque liste dans uniqueCrossTabs
comme argument à chain()
, qui les enchaînera, mais vous n'avez pas les listes dans des variables distinctes, vous utilisez donc l'opérateur *
pour développer la liste de listes en plusieurs arguments de liste.
Comme Jochen Ritzel l'a souligné dans les commentaires, chain.from_iterable()
est mieux adapté à cette opération, car il suppose un seul itérable d'itérables pour commencer. Votre code devient alors simplement:
uniqueCrossTabs = list(itertools.chain.from_iterable(uniqueCrossTabs))
Il divise la séquence en arguments séparés pour l'appel de fonction.
>>> 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)
Juste une autre façon d'expliquer le concept/de l'utiliser.
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]