Domanda

Mi chiedo se esiste una scorciatoia per creare un semplice elenco da un elenco di elenchi in Python.

Posso farlo in a for loop, ma forse c'è qualche "one-liner" interessante?L'ho provato con ridurre, ma ricevo un errore.

Codice

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)

Messaggio di errore

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
È stato utile?

Soluzione

Dato un elenco di liste l,

flat_list = [item for sublist in l for item in sublist]

che significa:

flat_list = []
for sublist in l:
    for item in sublist:
        flat_list.append(item)

è più veloce rispetto alle scorciatoie pubblicati finora. (l è la lista di appiattire.)

Qui è la funzione corrispondente:

flatten = lambda l: [item for sublist in l for item in sublist]

Come prova, è possibile utilizzare il modulo timeit nella libreria standard:

$ 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

Spiegazione: i collegamenti basati su + (compreso l'uso implicito in sum) sono, di necessità, O(L**2) quando ci sono L liste parziali - come la lista risultato intermedio mantiene sempre più, ad ogni passo un nuovo oggetto lista risultato intermedio ottiene assegnati, e tutti gli elementi nella precedente risultato intermedio devono essere copiati (così come alcune nuove aggiunte alla fine). Così, per semplicità e senza effettiva perdita di generalità, supponiamo di avere L liste parziali di elementi I ciascuno: il primo che gli elementi vengono copiati avanti e indietro L-1 volte, la seconda ho elementi volte L-2, e così via; numero totale di copie è I volte la somma di x per x da 1 a L esclusi, cioè I * (L**2)/2.

La lista di comprensione genera solo una lista, una volta, e le copie di ogni articolo sopra (dal suo luogo originario di residenza alla lista dei risultati) anche esattamente una volta.

Altri suggerimenti

È possibile utilizzare itertools.chain() :

>>> import itertools
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain(*list2d))

o, su Python> = 2.6, utilizzare itertools.chain.from_iterable() che non richiede l'estrazione, l'elenco:

>>> import itertools
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain.from_iterable(list2d))

Questo approccio è senza dubbio più leggibile di [item for sublist in l for item in sublist] e sembra essere più veloce troppo:

[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;import itertools' 'list(itertools.chain.from_iterable(l))'
10000 loops, best of 3: 24.2 usec per loop
[me@home]$ 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: 45.2 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 488 usec per loop
[me@home]$ 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: 522 usec per loop
[me@home]$ python --version
Python 2.7.3

Nota dell'autore : Questo è inefficiente. Ma il divertimento, perché monoidi sono impressionanti. Non è appropriato per il codice di produzione Python.

>>> sum(l, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Questo riassume solo gli elementi di iterabile passata nel primo argomento, trattando secondo argomento come valore iniziale della somma (se non dato, 0 è utilizzato, invece, e questo caso vi darà un errore).

Poiché si sta sommando le liste nidificate, è effettivamente ottenere [1,3]+[2,4] a seguito di sum([[1,3],[2,4]],[]), che è pari a [1,3,2,4].

Si noti che opera solo su elenchi di liste. Per gli elenchi di liste di liste, avrete bisogno di un'altra soluzione.

Ho provato soluzioni più suggerite con perfplot (un progetto della miniera, in sostanza, un wrapper timeit) , e ha trovato

functools.reduce(operator.iconcat, a, [])

per essere la soluzione più veloce. (operator.iadd è altrettanto veloce.)

 entrare descrizione dell'immagine qui


Il codice per riprodurre la trama:

import functools
import itertools
import numpy
import operator
import perfplot


def forfor(a):
    return [item for sublist in a for item in sublist]


def sum_brackets(a):
    return sum(a, [])


def functools_reduce(a):
    return functools.reduce(operator.concat, a)


def functools_reduce_iconcat(a):
    return functools.reduce(operator.iconcat, a, [])


def itertools_chain(a):
    return list(itertools.chain.from_iterable(a))


def numpy_flat(a):
    return list(numpy.array(a).flat)


def numpy_concatenate(a):
    return list(numpy.concatenate(a))


perfplot.show(
    setup=lambda n: [list(range(10))] * n,
    kernels=[
        forfor, sum_brackets, functools_reduce, functools_reduce_iconcat,
        itertools_chain, numpy_flat, numpy_concatenate
        ],
    n_range=[2**k for k in range(16)],
    logx=True,
    logy=True,
    xlabel='num lists'
    )
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]

Il metodo extend() nel tuo esempio modifica x invece di restituire un valore utile (che reduce() aspetta).

Un modo più veloce per fare la versione reduce sarebbe

>>> 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]

Ecco un approccio generale che si applica a numeri , stringhe , annidati le liste e misti contenitori.

Codice

#from typing import Iterable 
from collections import Iterable                            # < py38


def flatten(items):
    """Yield items from any nested iterable; see Reference."""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            for sub_x in flatten(x):
                yield sub_x
        else:
            yield x

Note :

  • In Python 3, yield from flatten(x) può sostituire for sub_x in flatten(x): yield sub_x
  • In Python 3.8, classi base astratte spostato dal collection.abc al modulo typing.

Demo

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(flatten(lst))                                         # nested lists
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

mixed = [[1, [2]], (3, 4, {5, 6}, 7), 8, "9"]              # numbers, strs, nested & mixed
list(flatten(mixed))
# [1, 2, 3, 4, 5, 6, 7, 8, '9']

Riferimento

  • Questa soluzione viene modificato da una ricetta in Beazley, D. e B. Jones. Ricetta 4.14, Python Cookbook 3rd Ed, O'Reilly Media Inc. Sebastopol, CA:.. 2013
  • SO inviare , forse la dimostrazione originale.

Se si vuole appiattire un data-struttura in cui non si sa quanto profonda è annidato si potrebbe usare iteration_utilities.deepflatten 1

>>> from iteration_utilities import deepflatten

>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(deepflatten(l, depth=1))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> l = [[1, 2, 3], [4, [5, 6]], 7, [8, 9]]
>>> list(deepflatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

E 'un generatore quindi è necessario eseguire il cast del risultato a un list o esplicitamente scorrere su di esso.


Per appiattire un solo livello e se ognuno degli elementi è di per sé iterabile è anche possibile utilizzare iteration_utilities.flatten che di per sé è solo un wrapper sottile intorno itertools.chain.from_iterable :

>>> from iteration_utilities import flatten
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(flatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Giusto per aggiungere un po 'timing (sulla base di Nico Schlömer risposta che non includevano la funzione presentata in questa risposta):

 entrare descrizione dell'immagine qui

E 'un grafico log-log per ospitare per la vasta gamma di valori calibrati. Per un ragionamento qualitativo: più basso è meglio

.

I risultati mostrano che se l'iterabile contiene solo pochi iterables interne poi sum sarà più veloce, ma per lunghi iterables solo il itertools.chain.from_iterable, iteration_utilities.deepflatten o la comprensione nidificato hanno prestazioni ragionevoli con itertools.chain.from_iterable essere il più veloce (come già notato da Nico Schlömer ).

from itertools import chain
from functools import reduce
from collections import Iterable  # or from collections.abc import Iterable
import operator
from iteration_utilities import deepflatten

def nested_list_comprehension(lsts):
    return [item for sublist in lsts for item in sublist]

def itertools_chain_from_iterable(lsts):
    return list(chain.from_iterable(lsts))

def pythons_sum(lsts):
    return sum(lsts, [])

def reduce_add(lsts):
    return reduce(lambda x, y: x + y, lsts)

def pylangs_flatten(lsts):
    return list(flatten(lsts))

def flatten(items):
    """Yield items from any nested iterable; see REF."""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            yield from flatten(x)
        else:
            yield x

def reduce_concat(lsts):
    return reduce(operator.concat, lsts)

def iteration_utilities_deepflatten(lsts):
    return list(deepflatten(lsts, depth=1))


from simple_benchmark import benchmark

b = benchmark(
    [nested_list_comprehension, itertools_chain_from_iterable, pythons_sum, reduce_add,
     pylangs_flatten, reduce_concat, iteration_utilities_deepflatten],
    arguments={2**i: [[0]*5]*(2**i) for i in range(1, 13)},
    argument_name='number of inner lists'
)

b.plot()

1 Disclaimer: io sono l'autore di quella libreria

Prendo la mia dichiarazione di nuovo. somma non è il vincitore. Anche se è più veloce quando la lista è piccola. Ma le prestazioni si riducono in modo significativo con le liste più grandi.

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10000'
    ).timeit(100)
2.0440959930419922

La versione somma è ancora in funzione per più di un minuto e non ha fatto la lavorazione ancora!

Per le liste di media:

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
20.126545906066895
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
22.242258071899414
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
16.449732065200806

Utilizzando piccole liste e timeit: number = 1000000

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
2.4598159790039062
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.5289170742034912
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.0598428249359131

Sembra che ci sia una confusione con operator.add! Quando si aggiungono due liste insieme, il termine corretto per questo è concat, non aggiungere. operator.concat è ciò che è necessario utilizzare.

Se stai pensando funzionale, è facile come questa ::

>>> from functools import reduce
>>> list2d = ((1, 2, 3), (4, 5, 6), (7,), (8, 9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)

Si vede ridurre gli effetti del tipo di sequenza, in modo che quando si fornisce una tupla, si ottiene indietro una tupla. Proviamo con un elenco ::

>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> reduce(operator.concat, list2d)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Ah, si ottiene indietro una lista.

Come per le prestazioni ::

>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> %timeit list(itertools.chain.from_iterable(list2d))
1000000 loops, best of 3: 1.36 µs per loop

from_iterable è abbastanza veloce! Ma non è il confronto per ridurre con concat.

>>> list2d = ((1, 2, 3),(4, 5, 6), (7,), (8, 9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop

Perché usi estendere?

reduce(lambda x, y: x+y, l)

Questo dovrebbe funzionare bene.

Consideriamo l'installazione del pacchetto more_itertools .

> pip install more_itertools

navi con un'implementazione per flatten ( href="https://more-itertools.readthedocs.io/en/stable/_modules/more_itertools/recipes.html#flatten" , dal itertools ricette ):

import more_itertools


lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.flatten(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

A partire dalla versione 2.4, è possibile appiattire più complicate, iterables nidificate con more_itertools.collapse ( fonte , contributo di abarnet).

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.collapse(lst)) 
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

lst = [[1, 2, 3], [[4, 5, 6]], [[[7]]], 8, 9]              # complex nesting
list(more_itertools.collapse(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

La ragione per la vostra funzione non ha funzionato: la estendono estende matrice in-place e non restituisce esso. È ancora possibile tornare x da lambda, usando qualche trucco:

reduce(lambda x,y: x.extend(y) or x, l)

. Nota: estendere è più efficiente di + sulle liste

Non reinventare la ruota se si utilizza Django :

>>> from django.contrib.admin.utils import flatten
>>> l = [[1,2,3], [4,5], [6]]
>>> flatten(l)
>>> [1, 2, 3, 4, 5, 6]

... Panda :

>>> from pandas.core.common import flatten
>>> list(flatten(l))

... itertools :

>>> import itertools
>>> flatten = itertools.chain.from_iterable
>>> list(flatten(l))

... Matplotlib

>>> from matplotlib.cbook import flatten
>>> list(flatten(l))

... Unipath :

>>> from unipath.path import flatten
>>> list(flatten(l))

... setuptools :

>>> from setuptools.namespaces import flatten
>>> list(flatten(l))
def flatten(l, a):
    for i in l:
        if isinstance(i, list):
            flatten(i, a)
        else:
            a.append(i)
    return a

print(flatten([[[1, [1,1, [3, [4,5,]]]], 2, 3], [4, 5],6], []))

# [1, 1, 1, 3, 4, 5, 2, 3, 4, 5, 6]

Una caratteristica cattivo della funzione di Anil sopra è che richiede all'utente di specificare sempre manualmente il secondo argomento di una lista [] vuota. Questo dovrebbe invece essere un difetto. A causa del modo oggetti Python funzionano, questi devono essere impostati all'interno della funzione, non negli argomenti.

Ecco una funzione di lavoro:

def list_flatten(l, a=None):
    #check a
    if a is None:
        #initialize with empty list
        a = []

    for i in l:
        if isinstance(i, list):
            list_flatten(i, a)
        else:
            a.append(i)
    return a

Test:

In [2]: lst = [1, 2, [3], [[4]],[5,[6]]]

In [3]: lst
Out[3]: [1, 2, [3], [[4]], [5, [6]]]

In [11]: list_flatten(lst)
Out[11]: [1, 2, 3, 4, 5, 6]

matplotlib.cbook.flatten() lavorerà per liste annidate anche se il nido più profondamente di quanto l'esempio.

import matplotlib
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
print(list(matplotlib.cbook.flatten(l)))
l2 = [[1, 2, 3], [4, 5, 6], [7], [8, [9, 10, [11, 12, [13]]]]]
print list(matplotlib.cbook.flatten(l2))

Risultato:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

Questa è 18x più veloce di sottolineatura ._ appiattire:.

Average time over 1000 trials of matplotlib.cbook.flatten: 2.55e-05 sec
Average time over 1000 trials of underscore._.flatten: 4.63e-04 sec
(time for underscore._)/(time for matplotlib.cbook) = 18.1233394636

La risposta accettata non ha funzionato per me quando si tratta di elenchi basati su testo di lunghezza variabile. Qui è un approccio alternativo che ha funzionato per me.

l = ['aaa', 'bb', 'cccccc', ['xx', 'yyyyyyy']]

risposta accettata che ha fatto non di lavoro:

flat_list = [item for sublist in l for item in sublist]
print(flat_list)
['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'c', 'xx', 'yyyyyyy']

Nuova soluzione proposta che ha lavoro per me:

flat_list = []
_ = [flat_list.extend(item) if isinstance(item, list) else flat_list.append(item) for item in l if item]
print(flat_list)
['aaa', 'bb', 'cccccc', 'xx', 'yyyyyyy']

versione ricorsiva

x = [1,2,[3,4],[5,[6,[7]]],8,9,[10]]

def flatten_list(k):
    result = list()
    for i in k:
        if isinstance(i,list):

            #The isinstance() function checks if the object (first argument) is an 
            #instance or subclass of classinfo class (second argument)

            result.extend(flatten_list(i)) #Recursive call
        else:
            result.append(i)
    return result

flatten_list(x)
#result = [1,2,3,4,5,6,7,8,9,10]

A seguito di sembrare più semplice per me:

>>> import numpy as np
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> print (np.concatenate(l))
[1 2 3 4 5 6 7 8 9]

Si può anche utilizzare del NumPy piatto :

import numpy as np
list(np.array(l).flat)

Modifica 2016/11/02: funziona solo quando sottoliste hanno dimensioni identiche

.

È possibile utilizzare numpy:
flat_list = list(np.concatenate(list_of_list))

underscore.py fan pacchetto

from underscore import _
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Si risolve tutti i problemi di appiattire (voce di lista none o nidificazione complessi)

from underscore import _
# 1 is none list item
# [2, [3]] is complex nesting
_.flatten([1, [2, [3]], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

È possibile installare underscore.py con pip

pip install underscore.py
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
flat_list = []
for i in list_of_list:
    flat_list+=i

Questo codice funziona anche bene come solo estendere la lista fino in fondo. Anche se è molto simile, ma hanno solo un ciclo for. Così si hanno meno complessità che l'aggiunta di 2 cicli for.

Se si è disposti a rinunciare a una piccola quantità di velocità per un look più pulito, allora si potrebbe utilizzare numpy.concatenate().tolist() o numpy.concatenate().ravel().tolist():

import numpy

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] * 99

%timeit numpy.concatenate(l).ravel().tolist()
1000 loops, best of 3: 313 µs per loop

%timeit numpy.concatenate(l).tolist()
1000 loops, best of 3: 312 µs per loop

%timeit [item for sublist in l for item in sublist]
1000 loops, best of 3: 31.5 µs per loop

È possibile saperne di più qui nella documentazione numpy.concatenate e numpy.ravel

soluzione più veloce che ho trovato (per elenco di grandi dimensioni in ogni caso):

import numpy as np
#turn list into an array and flatten()
np.array(l).flatten()

Fatto! Ovviamente si può trasformarlo di nuovo in una lista per lista eseguendo (l)

Questo non può essere il modo più efficiente, ma ho pensato di mettere una battuta (in realtà una due-liner). Entrambe le versioni funzionano su liste gerarchia annidati arbitrarie, e sfrutta caratteristiche del linguaggio (Python3.5) e ricorsione.

def make_list_flat (l):
    flist = []
    flist.extend ([l]) if (type (l) is not list) else [flist.extend (make_list_flat (e)) for e in l]
    return flist

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = make_list_flat(a)
print (flist)

L'uscita è

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Questo funziona in un primo modo di profondità. La ricorsione scende fino a trovare un elemento non elenco, estendendo poi la flist variabile locale e quindi ripristina al genitore. Ogni volta che flist viene restituito, si è estesa a flist del genitore nella lista di comprensione. Pertanto, alla radice, viene restituito un elenco semplice.

È possibile che uno crea diversi elenchi di locali e li restituisce che vengono utilizzati per estendere la lista del genitore. Penso che il modo in giro per questo può essere la creazione di un flist Gloabl, come qui di seguito.

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = []
def make_list_flat (l):
    flist.extend ([l]) if (type (l) is not list) else [make_list_flat (e) for e in l]

make_list_flat(a)
print (flist)

L'uscita è di nuovo

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Anche se io non sono sicuro che in questo momento circa l'efficienza.

Nota:Di seguito si applica a Python 3.3+ perché utilizza yield_from. six è anche un pacchetto di terze parti, sebbene sia stabile.In alternativa, potresti usare sys.version.


In caso di obj = [[1, 2,], [3, 4], [5, 6]], tutte le soluzioni qui sono buone, inclusa la comprensione delle liste e itertools.chain.from_iterable.

Tuttavia, considera questo caso leggermente più complesso:

>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]

Ci sono diversi problemi qui:

  • Un elemento, 6, è semplicemente uno scalare;non è iterabile, quindi i percorsi di cui sopra falliranno qui.
  • Un elemento, 'abc', È tecnicamente iterabile (all strs sono).Tuttavia, leggendo un po' tra le righe, non dovresti trattarlo come tale: vuoi trattarlo come un singolo elemento.
  • L'elemento finale, [8, [9, 10]] è esso stesso un iterabile annidato.Comprensione di elenchi di base e chain.from_iterable estrai solo "1 livello giù".

Puoi rimediare a questo come segue:

>>> from collections import Iterable
>>> from six import string_types

>>> def flatten(obj):
...     for i in obj:
...         if isinstance(i, Iterable) and not isinstance(i, string_types):
...             yield from flatten(i)
...         else:
...             yield i


>>> list(flatten(obj))
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10]

Qui, controlli che il sottoelemento (1) sia iterabile con Iterable, un ABC da itertools, ma voglio anche assicurarmi che (2) l'elemento lo sia non "simile a una corda".

Un altro approccio inusuale che funziona per etero e le liste omogenee di interi:

from typing import List


def flatten(l: list) -> List[int]:
    """Flatten an arbitrary deep nested list of lists of integers.

    Examples:
        >>> flatten([1, 2, [1, [10]]])
        [1, 2, 1, 10]

    Args:
        l: Union[l, Union[int, List[int]]

    Returns:
        Flatted list of integer
    """
    return [int(i.strip('[ ]')) for i in str(l).split(',')]

Un semplice metodo ricorsivo utilizzando reduce dal functools e l'operatore add sulle liste:

>>> from functools import reduce
>>> from operator import add
>>> flatten = lambda lst: [lst] if type(lst) is int else reduce(add, [flatten(ele) for ele in lst])
>>> flatten(l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Il flatten funzione prende in lst come parametro. Esso effettua il ciclo tutti gli elementi di lst fino a raggiungere numeri interi (può anche cambiare a int float, str, ecc per altri tipi di dati), che vengono aggiunti al valore di ritorno della ricorsione più esterno.

ricorsione, a differenza di metodi come loop for e monadi, è che è una soluzione generale non limitata dalla profondità elenco . Ad esempio, un elenco con profondità di 5 può essere appiattito allo stesso modo come l:

>>> l2 = [[3, [1, 2], [[[6], 5], 4, 0], 7, [[8]], [9, 10]]]
>>> flatten(l2)
[3, 1, 2, 6, 5, 4, 0, 7, 8, 9, 10]
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top