Jeg spekulerer på, om der findes en genvej til at lave en simpel liste ud af en liste af lister i Python.
Jeg kan gøre det i en for
loop, men måske er der en cool "one-liner"? Jeg har prøvet det med reduce()
, men jeg får en fejl.
Code
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)
Fejlemelding
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
Givet en liste af lister l
,
flat_list = [item for sublist i l for item i sublist]
hvilket betyder:
flat_list = []
for sublist in l:
for item in sublist:
flat_list.append(item)
er hurtigere end de genveje, der er blevet offentliggjort indtil nu. (l
er den liste der skal flades.)
Her er den tilsvarende funktion:
flatten = lambda l: [item for sublist in l for item in sublist]
Som bevis kan du bruge timeit
-modulet i standardbiblioteket:
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 3: 143 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 969 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 3: 1.1 msec per loop
Forklaring: Genvejene baseret på +
(herunder den implicitte brug i sum
) er nødvendigvis O(L**2)
, når der er L dellister -- da listen med mellemresultater bliver længere, bliver der ved hvert trin allokeret et nyt objekt i listen med mellemresultater, og alle elementer i det foregående mellemresultat skal kopieres over (samt et par nye, der tilføjes til sidst). Så lad os for enkelhedens skyld og uden at det går ud over generalismen sige, at vi har L dellister med I elementer hver: de første I elementer kopieres frem og tilbage L-1 gange, de andre I elementer L-2 gange osv.; det samlede antal kopier er I gange summen af x for x fra 1 til L udelukket, dvs. I * (L**2)/2
.
List comprehension genererer kun én liste én gang og kopierer hvert element over (fra dets oprindelige opholdssted til resultatlisten) også præcis én gang.
Note fra forfatteren: Dette er ineffektivt. Men det er sjovt, fordi monoids er fantastisk. Det er ikke egnet til produktion af Python-kode.
>>> sum(l, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Dette summerer bare elementerne af iterable overgivet i det første argument, idet det andet argument behandles som den oprindelige værdi af summen (hvis den ikke er givet, bruges 0
i stedet, og dette tilfælde vil give dig en fejl).
Fordi du summerer indlejrede lister, får du faktisk [1,3]+[2,4]
som resultat af sum([[1,3],[2,4]]],[])
, hvilket er lig med [1,3,2,4]
.
Bemærk, at det kun virker på lister af lister. For lister af lister af lister af lister, skal du bruge en anden løsning.
from functools import reduce #python 3
>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(lambda x,y: x+y,l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Metoden extend()
i dit eksempel ændrer x
i stedet for at returnere en brugbar værdi (som reduce()
forventer).
En hurtigere måde at lave reduce
-versionen på ville være
>>> import operator
>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(operator.concat, l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]