Ik vraag me af of er een sneltoets is om een eenvoudige lijst te maken uit een lijst van lijsten in Python.
Ik kan dat doen in een for
loop, maar misschien is er een coole "one-liner"? Ik heb het geprobeerd met reduce()
, maar ik krijg een foutmelding.
Code
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)
Foutmelding
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
Gegeven een lijst van lijsten l
,
flat_list = [item for sublist in l for item in sublist]
wat betekent:
flat_list = []
for sublist in l:
for item in sublist:
flat_list.append(item)
sneller is dan de sneltoetsen die tot nu toe zijn gepost. (l
is de lijst om af te vlakken).
Hier is de overeenkomstige functie:
flatten = lambda l: [item for sublist in l for item in sublist]
Als bewijs, kun je de timeit
module in de standaard bibliotheek gebruiken:
$ 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
Uitleg: de sneltoetsen gebaseerd op +
(inclusief het impliciete gebruik in sum
) zijn noodzakelijkerwijs O(L**2)
als er L sublijsten zijn -- als de lijst met tussenresultaten steeds langer wordt, wordt bij elke stap een nieuw object in de lijst met tussenresultaten gealloceerd, en alle items in het vorige tussenresultaat moeten worden gekopieerd (evenals een paar nieuwe die aan het eind worden toegevoegd). Dus, voor de eenvoud en zonder verlies van algemeenheid, stel je hebt L sublijsten van I items elk: de eerste I items worden L-1 keer heen en weer gekopieerd, de tweede I items L-2 keer, enzovoort; totaal aantal kopieën is I maal de som van x voor x van 1 tot L uitgesloten, dus I * (L**2)/2
.
De list comprehension genereert slechts één lijst, één keer, en kopieert elk item (van zijn oorspronkelijke plaats van verblijf naar de resultaatlijst) ook precies één keer.
Notitie van de auteur: Dit is inefficiënt. Maar leuk, want monoids zijn geweldig. Het's niet geschikt voor productie Python code.
>>> sum(l, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Dit telt gewoon de elementen op van de iterable die in het eerste argument wordt doorgegeven, waarbij het tweede argument wordt behandeld als de beginwaarde van de som (indien niet gegeven, wordt in plaats daarvan 0
gebruikt en in dit geval krijg je een foutmelding).
Omdat je geneste lijsten optelt, krijg je eigenlijk [1,3]+[2,4]
als resultaat van sum([[1,3],[2,4]],[])
, wat gelijk is aan [1,3,2,4]
.
Merk op dat dit alleen werkt voor lijsten van lijsten. Voor lijsten van lijsten van lijsten, heb je een andere oplossing nodig.
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]
De extend()
methode in je voorbeeld wijzigt x
in plaats van een bruikbare waarde terug te geven (wat reduce()
verwacht).
Een snellere manier om de reduce
versie te doen zou zijn
>>> 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]