Lista Planetoid

Tak, Wiem, że ten temat był już poruszany ( tutaj, Proszę., Proszę., tutaj ), ale z tego co wiem, wszystkie rozwiązania, poza jednym, zawodzą na takiej liście:

L = [[[1, 2, 3], [4, 5]], 6]

Gdzie żądanym wyjściem jest

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

A może nawet lepiej, iterator. Jedyne rozwiązanie, jakie widziałem, które działa dla dowolnego zagnieżdżenia znajduje się w tym pytaniu :

def flatten(x):
    result = []
    for el in x:
        if hasattr(el, "__iter__") and not isinstance(el, basestring):
            result.extend(flatten(el))
        else:
            result.append(el)
    return result

flatten(L)
Czy to najlepszy model? Czy coś przeoczyłem? Dowolne problemy?
Author: ShadowRanger, 2010-01-28

30 answers

Korzystanie z funkcji generatora może sprawić, że twój przykład będzie łatwiejszy do odczytania i prawdopodobnie zwiększy wydajność.

Python 2

def flatten(l):
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, basestring):
            for sub in flatten(el):
                yield sub
        else:
            yield el

Użyłem ITEROWALNEGO ABC dodanego w 2.6.

Python 3

W Pythonie 3, basestring nie jest więcej, ale możesz użyć krotki str i bytes, Aby uzyskać ten sam efekt.

Operator yield from zwraca element z generatora pojedynczo. Ta składnia delegowania do subgeneratora została dodana w 3.3

from collections.abc import Iterable

def flatten(l):
    for el in l:
        if isinstance(el, Iterable) and not isinstance(el, (str, bytes)):
            yield from flatten(el)
        else:
            yield el
 408
Author: Cristian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-10 12:33:33

Moje rozwiązanie:

import collections


def flatten(x):
    if isinstance(x, collections.Iterable):
        return [a for i in x for a in flatten(i)]
    else:
        return [x]

Trochę bardziej zwięzły, ale prawie taki sam.

 53
Author: Josh Lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-23 12:18:40

Generator wykorzystujący rekurencję i pisanie kaczek (aktualizacja dla Pythona 3):

def flatten(L):
    for item in L:
        try:
            yield from flatten(item)
        except TypeError:
            yield item

list(flatten([[[1, 2, 3], [4, 5]], 6]))
>>>[1, 2, 3, 4, 5, 6]
 41
Author: dansalmo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-08 15:01:25

Oto moja funkcjonalna wersja rekurencyjnego spłaszczania, która obsługuje zarówno krotki, jak i listy, i pozwala wrzucić dowolną mieszankę argumentów pozycyjnych. Zwraca generator, który wytwarza całą sekwencję w kolejności, arg przez arg:

flatten = lambda *n: (e for a in n
    for e in (flatten(*a) if isinstance(a, (tuple, list)) else (a,)))

Użycie:

l1 = ['a', ['b', ('c', 'd')]]
l2 = [0, 1, (2, 3), [[4, 5, (6, 7, (8,), [9]), 10]], (11,)]
print list(flatten(l1, -2, -1, l2))
['a', 'b', 'c', 'd', -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
 36
Author: samplebias,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-23 17:42:24

Wersja generatora niekonwencjonalnego rozwiązania @ unutbu, zgodnie z prośbą @Andrew w komentarzu:

def genflat(l, ltypes=collections.Sequence):
    l = list(l)
    i = 0
    while i < len(l):
        while isinstance(l[i], ltypes):
            if not l[i]:
                l.pop(i)
                i -= 1
                break
            else:
                l[i:i + 1] = l[i]
        yield l[i]
        i += 1

Nieco uproszczona wersja tego generatora:

def genflat(l, ltypes=collections.Sequence):
    l = list(l)
    while l:
        while l and isinstance(l[0], ltypes):
            l[0:1] = l[0]
        if l: yield l.pop(0)
 35
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-29 00:27:22

Ta wersja flatten unika limitu rekursji Pythona (i tym samym działa z arbitralnie głębokimi, zagnieżdżonymi iterabami). Jest to generator, który może obsługiwać ciągi i dowolne iteraby (nawet nieskończone).

import itertools as IT
import collections

def flatten(iterable, ltypes=collections.Iterable):
    remainder = iter(iterable)
    while True:
        first = next(remainder)
        if isinstance(first, ltypes) and not isinstance(first, (str, bytes)):
            remainder = IT.chain(first, remainder)
        else:
            yield first

Oto kilka przykładów demonstrujących jego użycie:

print(list(IT.islice(flatten(IT.repeat(1)),10)))
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

print(list(IT.islice(flatten(IT.chain(IT.repeat(2,3),
                                       {10,20,30},
                                       'foo bar'.split(),
                                       IT.repeat(1),)),10)))
# [2, 2, 2, 10, 20, 30, 'foo', 'bar', 1, 1]

print(list(flatten([[1,2,[3,4]]])))
# [1, 2, 3, 4]

seq = ([[chr(i),chr(i-32)] for i in range(ord('a'), ord('z')+1)] + list(range(0,9)))
print(list(flatten(seq)))
# ['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'E', 'f', 'F', 'g', 'G', 'h', 'H',
# 'i', 'I', 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N', 'o', 'O', 'p', 'P',
# 'q', 'Q', 'r', 'R', 's', 'S', 't', 'T', 'u', 'U', 'v', 'V', 'w', 'W', 'x', 'X',
# 'y', 'Y', 'z', 'Z', 0, 1, 2, 3, 4, 5, 6, 7, 8]

Chociaż flatten może obsługiwać nieskończone Generatory, nie może obsługiwać nieskończonego zagnieżdżania:

def infinitely_nested():
    while True:
        yield IT.chain(infinitely_nested(), IT.repeat(1))

print(list(IT.islice(flatten(infinitely_nested()), 10)))
# hangs
 29
Author: unutbu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-23 12:53:56

Oto kolejna odpowiedź, która jest jeszcze ciekawsza...

import re

def Flatten(TheList):
    a = str(TheList)
    b,_Anon = re.subn(r'[\[,\]]', ' ', a)
    c = b.split()
    d = [int(x) for x in c]

    return(d)

Zasadniczo konwertuje zagnieżdżoną listę na ciąg znaków, używa wyrażenia regularnego do usuwania zagnieżdżonej składni, a następnie konwertuje wynik z powrotem na (spłaszczoną) listę.

 11
Author: clay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-20 14:15:09
def flatten(xs):
    res = []
    def loop(ys):
        for i in ys:
            if isinstance(i, list):
                loop(i)
            else:
                res.append(i)
    loop(xs)
    return res
 10
Author: kev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-21 14:39:45

You could use deepflatten z pakietu 3rd party iteration_utilities:

>>> from iteration_utilities import deepflatten
>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(deepflatten(L))
[1, 2, 3, 4, 5, 6]

>>> list(deepflatten(L, types=list))  # only flatten "inner" lists
[1, 2, 3, 4, 5, 6]

Jest iteratorem, więc musisz go iteracją (na przykład przez owinięcie go za pomocą list lub użycie go w pętli). Jest napisany jako rozszerzenie C, dzięki czemu może być szybszy niż czyste podejście Pythona:

>>> %timeit list(deepflatten(L))
12.6 µs ± 298 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit list(deepflatten(L, types=list))
8.7 µs ± 139 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

>>> %timeit list(flatten(L))   # Cristian - Python 3.x approach from https://stackoverflow.com/a/2158532/5393381
86.4 µs ± 4.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit list(flatten(L))   # Josh Lee - https://stackoverflow.com/a/2158522/5393381
107 µs ± 2.99 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit list(genflat(L, list))  # Alex Martelli - https://stackoverflow.com/a/2159079/5393381
23.1 µs ± 710 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Jestem autorem iteration_utilities biblioteki.

 8
Author: MSeifert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-29 14:01:42

Fajnie było stworzyć funkcję, która mogłaby spłaszczać nieregularną listę w Pythonie, ale oczywiście po to jest Python (aby programowanie było zabawne). Poniższy generator działa dość dobrze z pewnymi zastrzeżeniami:

def flatten(iterable):
    try:
        for item in iterable:
            yield from flatten(item)
    except TypeError:
        yield iterable

Spłaszczy typy danych, które możesz chcieć zostawić w spokoju (jak bytearray, bytes, i str obiektów). Ponadto kod opiera się na fakcie, że żądanie iteratora od nie-iterowalnego wywołuje TypeError.

>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> def flatten(iterable):
    try:
        for item in iterable:
            yield from flatten(item)
    except TypeError:
        yield iterable


>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>>

Edit:

I nie zgadzam się z poprzednim wdrożeniem. Problem polega na tym, że nie powinieneś być w stanie spłaścić czegoś, co nie jest iterowalne. To jest mylące i daje błędne wrażenie argumentu.

>>> list(flatten(123))
[123]
>>>

Następujący generator jest prawie taki sam jak pierwszy, ale nie ma problemu z próbą spłaszczenia obiektu nie-iterowalnego. Zawodzi, jak można by się spodziewać, gdy podano mu niewłaściwy argument.

def flatten(iterable):
    for item in iterable:
        try:
            yield from flatten(item)
        except TypeError:
            yield item

Testowanie generatora działa dobrze z listą, która była pod warunkiem. Jednak nowy kod wywoła TypeError, gdy dany jest obiekt nie-iterowalny. Poniżej przedstawiono przykład nowego zachowania.

>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>> list(flatten(123))
Traceback (most recent call last):
  File "<pyshell#32>", line 1, in <module>
    list(flatten(123))
  File "<pyshell#27>", line 2, in flatten
    for item in iterable:
TypeError: 'int' object is not iterable
>>>
 7
Author: Noctis Skytower,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-28 13:51:28

Mimo, że wybrano elegancką i bardzo pythoniczną odpowiedź, przedstawiłbym swoje rozwiązanie tylko do recenzji:

def flat(l):
    ret = []
    for i in l:
        if isinstance(i, list) or isinstance(i, tuple):
            ret.extend(flat(i))
        else:
            ret.append(i)
    return ret

Proszę powiedzieć, jak dobry lub zły jest ten kod?

 6
Author: Xolve,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-07 22:32:07

Wolę proste odpowiedzi. Żadnych generatorów. Brak ograniczeń rekurencji i rekurencji. Tylko iteracja:

def flatten(TheList):
    listIsNested = True

    while listIsNested:                 #outer loop
        keepChecking = False
        Temp = []

        for element in TheList:         #inner loop
            if isinstance(element,list):
                Temp.extend(element)
                keepChecking = True
            else:
                Temp.append(element)

        listIsNested = keepChecking     #determine if outer loop exits
        TheList = Temp[:]

    return TheList

To działa z dwiema listami: wewnętrzną pętlą for i zewnętrzną pętlą while.

Wewnętrzna pętla for przechodzi przez Listę. Jeśli znajdzie element list, to (1) użyje list.extend() do spłaszczenia części pierwszego poziomu zagnieżdżenia i (2) przełącza keepChecking na True. keepchecking służy do kontrolowania zewnętrznej pętli while. Jeśli zewnętrzna pętla zostanie ustawiona na true, wyzwala wewnętrzną pętla na kolejny przejazd.

Te przejścia będą działać, dopóki nie zostaną znalezione żadne zagnieżdżone listy. Gdy w końcu dojdzie do przejścia, w którym nie znaleziono żadnego, keepChecking nigdy nie zostanie potknięty do true, co oznacza, że listIsNested pozostaje false, a zewnętrzna pętla while wychodzi.

Spłaszczona lista jest zwracana.

Test-run

flatten([1,2,3,4,[100,200,300,[1000,2000,3000]]])

[1, 2, 3, 4, 100, 200, 300, 1000, 2000, 3000]

 4
Author: clay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-13 04:16:53

Oto prosta funkcja, która spłaszcza listy dowolnej głębokości. Brak rekursji, aby uniknąć przepełnienia stosu.

from copy import deepcopy

def flatten_list(nested_list):
    """Flatten an arbitrarily nested list, without recursion (to avoid
    stack overflows). Returns a new list, the original list is unchanged.

    >> list(flatten_list([1, 2, 3, [4], [], [[[[[[[[[5]]]]]]]]]]))
    [1, 2, 3, 4, 5]
    >> list(flatten_list([[1, 2], 3]))
    [1, 2, 3]

    """
    nested_list = deepcopy(nested_list)

    while nested_list:
        sublist = nested_list.pop(0)

        if isinstance(sublist, list):
            nested_list = sublist + nested_list
        else:
            yield sublist
 4
Author: Wilfred Hughes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-10 14:55:11

Nie przejrzałem tutaj wszystkich dostępnych odpowiedzi, ale oto jedna linijka, którą wymyśliłem, zapożyczając ze sposobu przetwarzania listy pierwszej i reszty

def flatten(l): return flatten(l[0]) + (flatten(l[1:]) if len(l) > 1 else []) if type(l) is list else [l]

Oto jeden prosty i jeden nie taki prosty przypadek -

>>> flatten([1,[2,3],4])
[1, 2, 3, 4]

>>> flatten([1, [2, 3], 4, [5, [6, {'name': 'some_name', 'age':30}, 7]], [8, 9, [10, [11, [12, [13, {'some', 'set'}, 14, [15, 'some_string'], 16], 17, 18], 19], 20], 21, 22, [23, 24], 25], 26, 27, 28, 29, 30])
[1, 2, 3, 4, 5, 6, {'age': 30, 'name': 'some_name'}, 7, 8, 9, 10, 11, 12, 13, set(['set', 'some']), 14, 15, 'some_string', 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
>>> 
 3
Author: Shreyas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-08 13:24:02

Próbując odpowiedzieć na takie pytanie, naprawdę musisz podać ograniczenia kodu, który proponujesz jako rozwiązanie. Gdyby chodziło tylko o wydajność nie miałbym nic przeciwko, ale większość kodów zaproponowanych jako rozwiązanie (w tym przyjęta odpowiedź) nie spłaszcza żadnej listy, która ma głębokość większą niż 1000.

Kiedy mówię większość kodów mam na myśli wszystkie kody, które używają jakiejkolwiek formy rekurencji (lub wywołują standardową funkcję biblioteczną, która jest rekurencyjna). Wszystkie te kody błąd, ponieważ dla każdego wywołania rekurencyjnego, stos (wywołania) rośnie o jedną jednostkę, a (domyślnie) stos wywołań Pythona ma rozmiar 1000.

Jeśli nie jesteś zbyt zaznajomiony ze stosem wywołań, być może poniższe wskazówki pomogą(w przeciwnym razie możesz po prostu przewinąć do implementacji ).

Wywołaj rozmiar stosu i programowanie rekurencyjne (analogia dungeon)

Znalezienie skarbu i wyjście

Wyobraź sobie, że wchodzisz do ogromnego lochu z numerowanymi pokoje , Szukam skarbu. Nie znasz tego miejsca, ale masz wskazówki, jak znaleźć skarb. Każde wskazanie jest zagadką (trudność jest różna, ale nie można przewidzieć, jak trudne będą). Decydujesz się pomyśleć trochę o strategii, aby zaoszczędzić czas, robisz dwie uwagi:

    [59]}trudno (długo) znaleźć skarb, ponieważ będziesz musiał rozwiązać (potencjalnie trudne) zagadki, aby się tam dostać.
  1. gdy skarb znalazł, wracając do Wejście Może być łatwe, wystarczy użyć tej samej ścieżki w innym kierunku(choć wymaga to trochę pamięci, aby przypomnieć sobie ścieżkę).
Gdy wejdziesz do lochu, zauważysz mały notatnik. Decydujesz się na jego użycie, aby zapisać każdy pokój, z którego wyjdziesz po rozwiązaniu zagadki (przy wejściu do nowego pokoju), w ten sposób będziesz mógł wrócić do wejścia. To genialny pomysł, nie wydasz nawet centa na wdrożenie swojej strategii.

Ty wejdź do lochu, rozwiązując z wielkim sukcesem pierwsze 1001 zagadek, ale nadchodzi coś, czego nie zaplanowałeś, nie masz już miejsca w pożyczonym notatniku. Decydujesz się porzucić swoją misję, ponieważ wolisz nie mieć skarbu, niż być zagubionym na zawsze w lochu (wygląda to naprawdę mądrze).

Wykonywanie programu rekurencyjnego

Zasadniczo, to dokładnie to samo, co znalezienie skarbu. Dungeon jest pamięcią komputera , twoim celem teraz nie jest znalezienie skarbu, ale obliczyć jakąś funkcję (find f (x) dla danego x ). Wskazania są po prostu podprogramami, które pomogą Ci rozwiązać f (x) . Twoja strategia jest taka sama jak strategia stosu wywołań , notatnik jest stosem, pokoje są adresami zwrotnymi funkcji:
x = ["over here", "am", "I"]
y = sorted(x) # You're about to enter a room named `sorted`, note down the current room address here so you can return back: 0x4004f4 (that room address looks weird)
# Seems like you went back from your quest using the return address 0x4004f4
# Let's see what you've collected 
print(' '.join(y))

Problem, który napotkałeś w lochu będzie taki sam tutaj, stos wywołań ma skończoną wielkość (tutaj 1000) i dlatego, jeśli jeśli wpiszesz zbyt wiele funkcji bez powrotu, wypełnisz stos połączeń i pojawi się błąd, który wygląda jak "drogi poszukiwaczu przygód, bardzo mi przykro, ale twój notatnik jest pełny": RecursionError: maximum recursion depth exceeded. Zauważ, że nie potrzebujesz rekurencji, aby wypełnić stos wywołań, ale jest bardzo mało prawdopodobne, aby program nie rekurencyjny wywołał 1000 funkcji bez powrotu. Ważne jest również, aby zrozumieć, że po powrocie z funkcji stos wywołań jest uwalniany od używanego adresu (stąd nazwa " stos", adres zwrotny jest wpychany przed wprowadzeniem funkcji i wyciągany podczas powrotu). W specjalnym przypadku prostej rekurencji (funkcja f, która wywołuje się raz-w kółko -), będziesz wchodzić f w kółko, aż obliczenia zostaną zakończone (dopóki skarb nie zostanie znaleziony) i powracać z f aż wrócisz do miejsca, w którym wywołałeś f w pierwszej kolejności. Stos połączeń nigdy nie zostanie uwolniony od niczego, aż do końca, w którym zostanie uwolniony od wszystkich adresów zwrotnych jeden po drugim.

Jak uniknąć tego problemu?

To właściwie całkiem proste: "nie używaj rekurencji, jeśli nie wiesz, jak głęboko może ona zajść". Nie zawsze jest to prawdą, ponieważ w niektórych przypadkach można zoptymalizować rekurencję wywołania ogonowego (TCO) . Ale w Pythonie tak nie jest i nawet "dobrze napisana" funkcja rekurencyjna będzie optymalizować użycie stosu. Jest ciekawy post od Guido na ten temat: Rekurencja ogonowa Eliminacje.

Istnieje technika, której możesz użyć do uczynienia dowolnej iteracyjnej funkcji rekurencyjnej. Przynieś swój własny notatnik. Na przykład, w naszym konkretnym przypadku po prostu badamy listę, wejście do pokoju jest równoznaczne z wejściem do sublisty, pytanie, które powinieneś sobie zadać, to Jak mogę wrócić z listy do listy nadrzędnej? odpowiedź nie jest tak złożona, powtórz następujące czynności, aż stack będzie pusty:

  1. push the current list address and index in a stack when enter a new sublist (zauważ, że adres listy+indeks jest również adresem, dlatego po prostu używamy dokładnie tej samej techniki używanej przez stos wywołań);
  2. Nie jest to jednak żaden problem, ponieważ nie jest to możliwe.]}
  3. po pełnym zbadaniu listy, wróć do listy nadrzędnej, używając stack return address (and index).

Należy również zauważyć, że jest to odpowiednik DFS w drzewo, w którym niektóre węzły są podlistami A = [1, 2], a niektóre są prostymi elementami: 0, 1, 2, 3, 4 (dla L = [0, [1,2], 3, 4]). Drzewo wygląda tak:

                    L
                    |
           -------------------
           |     |     |     |
           0   --A--   3     4
               |   |
               1   2

DFS traversal pre-order to: L, 0, A, 1, 2, 3, 4. Pamiętaj, że aby zaimplementować iteracyjny DFS, "potrzebujesz" stosu. Zaproponowane przeze mnie wcześniej wdrożenie skutkuje posiadaniem następujących stanów (dla stack i flat_list):

init.:  stack=[(L, 0)]
**0**:  stack=[(L, 0)],         flat_list=[0]
**A**:  stack=[(L, 1), (A, 0)], flat_list=[0]
**1**:  stack=[(L, 1), (A, 0)], flat_list=[0, 1]
**2**:  stack=[(L, 1), (A, 1)], flat_list=[0, 1, 2]
**3**:  stack=[(L, 2)],         flat_list=[0, 1, 2, 3]
**3**:  stack=[(L, 3)],         flat_list=[0, 1, 2, 3, 4]
return: stack=[],               flat_list=[0, 1, 2, 3, 4]

W tym przykładzie maksymalny rozmiar stosu wynosi 2, ponieważ lista wejściowa (a zatem drzewo) ma głębokość 2.

Implementacja

Dla implementacji, w Pythonie można nieco uprościć używając iteratorów zamiast prostych list. Odwołania do (Pod)iteratorów będą używane do przechowywania adresów zwrotnych podlistek (zamiast mieć zarówno adres listy, jak i indeks). Nie jest to duża różnica, ale czuję, że jest to bardziej czytelne (a także nieco szybsze): {]}

def flatten(iterable):
    return list(items_from(iterable))

def items_from(iterable):
    cursor_stack = [iter(iterable)]
    while cursor_stack:
        sub_iterable = cursor_stack[-1]
        try:
            item = next(sub_iterable)
        except StopIteration:   # post-order
            cursor_stack.pop()
            continue
        if is_list_like(item):  # pre-order
            cursor_stack.append(iter(item))
        elif item is not None:
            yield item          # in-order

def is_list_like(item):
    return isinstance(item, list)

Zauważ również, że w is_list_like mam isinstance(item, list), które można zmienić, aby obsłużyć więcej wejść typy, tutaj chciałem tylko mieć najprostszą wersję, w której (iterable) jest tylko lista. Ale można też to zrobić:

def is_list_like(item):
    try:
        iter(item)
        return not isinstance(item, str)  # strings are not lists (hmm...) 
    except TypeError:
        return False

To traktuje łańcuchy jako "proste elementy" i dlatego flatten_iter([["test", "a"], "b]) zwróci ["test", "a", "b"], a nie ["t", "e", "s", "t", "a", "b"]. Zauważ, że w takim przypadku iter(item) jest wywoływany dwa razy na każdym elemencie, udawajmy, że jest to ćwiczenie dla czytelnika, aby uczynić to czystszym.

Testy i uwagi na temat innych implementacji

Na koniec pamiętaj, że nie możesz wydrukować nieskończenie zagnieżdżonego list L using print(L) ponieważ wewnętrznie będzie używać wywołań rekurencyjnych do __repr__ (RecursionError: maximum recursion depth exceeded while getting the repr of an object). Z tego samego powodu rozwiązania flatten Z udziałem {[35] } zawiodą z tym samym komunikatem o błędzie.

Jeśli chcesz przetestować swoje rozwiązanie, możesz użyć tej funkcji do wygenerowania prostej listy zagnieżdżonej:

def build_deep_list(depth):
    """Returns a list of the form $l_{depth} = [depth-1, l_{depth-1}]$
    with $depth > 1$ and $l_0 = [0]$.
    """
    sub_list = [0]
    for d in range(1, depth):
        sub_list = [d, sub_list]
    return sub_list

Co daje: build_deep_list(5) >>> [4, [3, [2, [1, [0]]]]].

 3
Author: cglacet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-02 10:30:05

Oto implementacja compiler.ast.flatten w 2.7.5:

def flatten(seq):
    l = []
    for elt in seq:
        t = type(elt)
        if t is tuple or t is list:
            for elt2 in flatten(elt):
                l.append(elt2)
        else:
            l.append(elt)
    return l

Są lepsze, szybsze metody (jeśli tu dotarłeś, to już je widziałeś)

Uwaga:

Przestarzały od wersji 2.6: pakiet kompilatora został usunięty w Pythonie 3.

 2
Author: pradyunsg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-27 13:05:57

Dziwię się, że nikt o tym nie pomyślał. Cholerna rekurencja nie rozumiem rekurencyjnych odpowiedzi, które zrobili zaawansowani ludzie. tak czy siak, to moja próba. zastrzeżenie jest bardzo specyficzne dla przypadku użycia OP

import re

L = [[[1, 2, 3], [4, 5]], 6]
flattened_list = re.sub("[\[\]]", "", str(L)).replace(" ", "").split(",")
new_list = list(map(int, flattened_list))
print(new_list)

Wyjście:

[1, 2, 3, 4, 5, 6]
 2
Author: Zion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-14 06:28:45

Nie widze tu czegos takiego, a wlasnie dotarlo tu z zamknietego pytania na ten sam temat, ale dlaczego nie zrobic czegos takiego (jesli znasz typ listy, ktora chcesz podzielic):

>>> a = [1, 2, 3, 5, 10, [1, 25, 11, [1, 0]]]    
>>> g = str(a).replace('[', '').replace(']', '')    
>>> b = [int(x) for x in g.split(',') if x.strip()]

Trzeba by znać rodzaj elementów, ale myślę, że można to uogólnić i jeśli chodzi o szybkość, myślę, że byłoby to szybsze.

 1
Author: Bogdan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-25 17:36:51

Totally hacky but I think it would work (depending on your data_type)

flat_list = ast.literal_eval("[%s]"%re.sub("[\[\]]","",str(the_list)))
 1
Author: Joran Beasley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-28 17:54:20

Oto kolejne podejście py2, Nie jestem pewien, czy jest najszybsze, czy najbardziej eleganckie, czy najbezpieczniejsze ...

from collections import Iterable
from itertools import imap, repeat, chain


def flat(seqs, ignore=(int, long, float, basestring)):
    return repeat(seqs, 1) if any(imap(isinstance, repeat(seqs), ignore)) or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))

Może ignorować dowolny konkretny (lub Pochodny) typ, który chcesz, zwraca iterator, więc możesz go przekonwertować na dowolny konkretny kontener, taki jak list, tuple, dict lub po prostu użyć go w celu zmniejszenia śladu pamięci, na dobre lub na złe może obsługiwać początkowe nie-iteracyjne obiekty, takie jak int ...

Uwaga większość podnoszenia ciężarów odbywa się w C, ponieważ z tego co wiem w ten sposób implementowane są itertools, więc chociaż jest rekurencyjny, AFAIK nie jest ograniczony przez głębokość rekurencji Pythona, ponieważ wywołania funkcji mają miejsce w C, chociaż nie oznacza to, że jesteś ograniczony przez pamięć, szczególnie w OS X, gdzie rozmiar stosu ma twardy limit od dziś (OS X Mavericks) ...

Istnieje nieco szybsze podejście, ale mniej przenośne metody, używaj go tylko wtedy, gdy możesz założyć, że podstawowe elementy wejścia mogą być wyraźnie określone w przeciwnym razie, otrzymasz nieskończona rekurencja i OS X z ograniczonym rozmiarem stosu dość szybko spowodują błąd segmentacji ...

def flat(seqs, ignore={int, long, float, str, unicode}):
    return repeat(seqs, 1) if type(seqs) in ignore or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))

Tutaj używamy zestawów, aby sprawdzić typ, więc potrzeba O(1) vs o (liczba typów), aby sprawdzić, czy element powinien być ignorowany, chociaż oczywiście każda wartość z pochodnym typem podanych ignorowanych typów zawiedzie, dlatego jej użycie str, unicode więc używaj go ostrożnie ...

Testy:

import random

def test_flat(test_size=2000):
    def increase_depth(value, depth=1):
        for func in xrange(depth):
            value = repeat(value, 1)
        return value

    def random_sub_chaining(nested_values):
        for values in nested_values:
            yield chain((values,), chain.from_iterable(imap(next, repeat(nested_values, random.randint(1, 10)))))

    expected_values = zip(xrange(test_size), imap(str, xrange(test_size)))
    nested_values = random_sub_chaining((increase_depth(value, depth) for depth, value in enumerate(expected_values)))
    assert not any(imap(cmp, chain.from_iterable(expected_values), flat(chain(((),), nested_values, ((),)))))

>>> test_flat()
>>> list(flat([[[1, 2, 3], [4, 5]], 6]))
[1, 2, 3, 4, 5, 6]
>>>  

$ uname -a
Darwin Samys-MacBook-Pro.local 13.3.0 Darwin Kernel Version 13.3.0: Tue Jun  3 21:27:35 PDT 2014; root:xnu-2422.110.17~1/RELEASE_X86_64 x86_64
$ python --version
Python 2.7.5
 1
Author: Samy Vilar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-18 16:14:38

Bez użycia żadnej biblioteki:

def flat(l):
    def _flat(l, r):    
        if type(l) is not list:
            r.append(l)
        else:
            for i in l:
                r = r + flat(i)
        return r
    return _flat(l, [])



# example
test = [[1], [[2]], [3], [['a','b','c'] , [['z','x','y']], ['d','f','g']], 4]    
print flat(test) # prints [1, 2, 3, 'a', 'b', 'c', 'z', 'x', 'y', 'd', 'f', 'g', 4]
 1
Author: Nir Alfasi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-07 17:58:00

Używając itertools.chain:

import itertools
from collections import Iterable

def list_flatten(lst):
    flat_lst = []
    for item in itertools.chain(lst):
        if isinstance(item, Iterable):
            item = list_flatten(item)
            flat_lst.extend(item)
        else:
            flat_lst.append(item)
    return flat_lst

Lub bez łańcucha:

def flatten(q, final):
    if not q:
        return
    if isinstance(q, list):
        if not isinstance(q[0], list):
            final.append(q[0])
        else:
            flatten(q[0], final)
        flatten(q[1:], final)
    else:
        final.append(q)
 1
Author: Saksham Varma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-09 22:04:16

Użyłem rekurencyjnego do rozwiązania zagnieżdżona lista o dowolnej głębokości

def combine_nlist(nlist,init=0,combiner=lambda x,y: x+y):
    '''
    apply function: combiner to a nested list element by element(treated as flatten list)
    '''
    current_value=init
    for each_item in nlist:
        if isinstance(each_item,list):
            current_value =combine_nlist(each_item,current_value,combiner)
        else:
            current_value = combiner(current_value,each_item)
    return current_value

Więc po zdefiniowaniu funkcji combine_nlist, łatwo jest użyć tej funkcji do flatting. Możesz też połączyć go w jedną funkcję. Podoba mi się moje rozwiązanie, ponieważ można je zastosować do dowolnej zagnieżdżonej listy.

def flatten_nlist(nlist):
    return combine_nlist(nlist,[],lambda x,y:x+[y])

Wynik

In [379]: flatten_nlist([1,2,3,[4,5],[6],[[[7],8],9],10])
Out[379]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 1
Author: Oldyoung,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-09 11:13:41

Najprostszym sposobem jest użycie biblioteki morph przy użyciu pip install morph.

Kod to:

import morph

list = [[[1, 2, 3], [4, 5]], 6]
flattened_list = morph.flatten(list)  # returns [1, 2, 3, 4, 5, 6]
 1
Author: YPCrumble,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-15 20:03:50

Zdaję sobie sprawę, że istnieje już wiele niesamowitych odpowiedzi, ale chciałem dodać odpowiedź, która wykorzystuje metodę programowania funkcyjnego rozwiązania pytania. W tej odpowiedzi korzystam z podwójnej rekurencji:

def flatten_list(seq):
    if not seq:
        return []
    elif isinstance(seq[0],list):
        return (flatten_list(seq[0])+flatten_list(seq[1:]))
    else:
        return [seq[0]]+flatten_list(seq[1:])

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

Wyjście:

[1, 2, 3, 4, 5, 6, 7]
 1
Author: Leo wahyd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-03 15:46:27

Nie jestem pewien, czy jest to koniecznie szybsze czy bardziej skuteczne, ale to jest to, co robię:

def flatten(lst):
    return eval('[' + str(lst).replace('[', '').replace(']', '') + ']')

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

Funkcja flatten zamienia listę w ciąg znaków, usuwa Wszystkie nawiasy kwadratowe, dołącza nawiasy kwadratowe z powrotem na końce i zamienia ją z powrotem w listę.

Chociaż, gdybyś wiedział, że masz nawiasy kwadratowe na liście w łańcuchach, jak [[1, 2], "[3, 4] and [5]"], musiałbyś zrobić coś innego.

 1
Author: diligar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-13 02:32:08

Jest to proste zaimplementowanie flatten na python2

flatten=lambda l: reduce(lambda x,y:x+y,map(flatten,l),[]) if isinstance(l,list) else [l]

test=[[1,2,3,[3,4,5],[6,7,[8,9,[10,[11,[12,13,14]]]]]],]
print flatten(test)

#output [1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
 1
Author: Statham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-28 15:48:09

Po prostu użyj funcy biblioteka: pip install funcy

import funcy


funcy.flatten([[[[1, 1], 1], 2], 3]) # returns generator
funcy.lflatten([[[[1, 1], 1], 2], 3]) # returns list
 1
Author: ADR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-23 13:32:02

To spłaszczy listę lub słownik (lub listę list lub słowników słowników itp.). Zakłada, że wartości są łańcuchami i tworzy łańcuch, który łączy każdy element z argumentem separatora. Jeśli chcesz, możesz użyć separatora, aby podzielić wynik na obiekt listy. Używa rekurencji, jeśli następna wartość jest listą lub łańcuchem znaków. Użyj argumentu key, aby określić, czy chcesz mieć klucze, czy wartości (Ustaw klucz na false) z obiektu dictionary.

def flatten_obj(n_obj, key=True, my_sep=''):
    my_string = ''
    if type(n_obj) == list:
        for val in n_obj:
            my_sep_setter = my_sep if my_string != '' else ''
            if type(val) == list or type(val) == dict:
                my_string += my_sep_setter + flatten_obj(val, key, my_sep)
            else:
                my_string += my_sep_setter + val
    elif type(n_obj) == dict:
        for k, v in n_obj.items():
            my_sep_setter = my_sep if my_string != '' else ''
            d_val = k if key else v
            if type(v) == list or type(v) == dict:
                my_string += my_sep_setter + flatten_obj(v, key, my_sep)
            else:
                my_string += my_sep_setter + d_val
    elif type(n_obj) == str:
        my_sep_setter = my_sep if my_string != '' else ''
        my_string += my_sep_setter + n_obj
        return my_string
    return my_string

print(flatten_obj(['just', 'a', ['test', 'to', 'try'], 'right', 'now', ['or', 'later', 'today'],
                [{'dictionary_test': 'test'}, {'dictionary_test_two': 'later_today'}, 'my power is 9000']], my_sep=', ')

just, a, test, to, try, right, now, or, later, today, dictionary_test, dictionary_test_two, my power is 9000
 1
Author: Matt Farguson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-23 13:40:09

Jestem głupi, więc dam "głupie" rozwiązanie. Cała ta rekurencja rani mój mózg.

flattened_list = []
nested_list = [[[1, 2, 3], [4, 5]], 6]

def flatten(nested_list, container):
    for item in nested_list:
        if isintance(item, list):
            flatten(item, container)
        else:
            container.append(item)

>>> flatten(nested_list, flattened_list)
>>> flattened_list
[1, 2, 3, 4, 5, 6]

Rozumiem, że używa efektu ubocznego, ale cóż, to najlepsze z mojego zrozumienia rekurencji może iść

 1
Author: halcyonjuly7,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-04 11:12:13