Dlaczego Python nie ma funkcji „spłaszczania” list?

39

Erlang i Ruby mają funkcje spłaszczania tablic. Wydaje się, że jest to takie proste i przydatne narzędzie, które można dodać do języka. Można to zrobić:

>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> mess.flatten()
[1, 2, 3, 4, 5, 6]

Lub nawet:

>>> import itertools
>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> list(itertools.flatten(mess))
[1, 2, 3, 4, 5, 6]

Zamiast tego w Pythonie trzeba przebrnąć przez pisanie funkcji spłaszczania tablic od zera. Wydaje mi się to głupie, spłaszczanie tablic jest tak powszechną rzeczą. To tak, jakby napisać niestandardową funkcję do łączenia dwóch tablic.

Googlowałem to bezowocnie, więc pytam tutaj; czy jest jakiś szczególny powód, dla którego dojrzały język, taki jak Python 3, który zawiera sto tysięcy różnych baterii, nie zapewnia prostej metody spłaszczania tablic? Czy pomysł włączenia takiej funkcji został w pewnym momencie omówiony i odrzucony?

Hubro
źródło
2
@detly: Ostatnio zdarzyło mi się spóźnić spłaszczanie, gdy używam kilku zapytań do pobierania danych z różnych źródeł. Każde zapytanie zwraca listę słowników, więc na koniec mam listę list słowników, które można przekształcić w listę słowników. Użyłem pętli +, extendale spłaszczenie byłoby znacznie bardziej eleganckie. Ostrzegam jednak, czy ten wzorzec jest na tyle powszechny, że uzasadnia spłaszczenie w standardowej bibliotece.
Giorgio
4
„Mam na myśli, wyobraź sobie, że wprowadzasz błąd w kodzie, który przypadkowo zmienia strukturę twoich danych. Spłaszczanie będzie nadal działać, ale generuje całkowicie niepoprawne wyniki.”: To jeden z powodów, dla których lubię języki o typie statycznym. ;-)
Giorgio
2
@BryanOakley Zobacz także poprzedni komentarz (choć nie w przypadku list wielopoziomowych, spłaszczanie ogólnie jest powszechne)
Izkata
3
Jest wbudowany w Mathemaica i używam go intensywnie.
Per Alexandersson,

Odpowiedzi:

34

Propozycje flattendodania funkcji do standardowej biblioteki pojawiają się od czasu do czasu na listach dyskusyjnych python-dev i python-ideas . Programiści Python zwykle odpowiadają następującymi punktami:

  1. Jednopoziomowe spłaszczanie (zamienianie iterowalnego elementu iteracyjnego w pojedynczy iterowalny) jest trywialnym wyrażeniem jednowierszowym (x for y in z for x in y)i w każdym razie znajduje się już w standardowej bibliotece pod nazwą itertools.chain.from_iterable.

  2. Jakie są przypadki zastosowania spłaszczania wielopoziomowego ogólnego zastosowania? Czy to naprawdę wystarczające, aby funkcja mogła zostać dodana do standardowej biblioteki?

  3. Jak wielopoziomowe spłaszczanie ogólnego przeznaczenia decyduje, kiedy spłaszczyć, a kiedy zostawić w spokoju? Możesz pomyśleć, że reguła taka jak „spłaszcz wszystko, co obsługuje iterowalny interfejs”, zadziała, ale doprowadziłoby to do nieskończonej pętli flatten('a').

Zobacz na przykład Raymond Hettinger :

Zostało to omówione ad nauseam na comp.lang.python. Wydaje się, że ludzie lubią pisać własne wersje spłaszczania, niż znajdować uzasadnione przypadki użycia, które nie mają jeszcze trywialnych rozwiązań.

Spłaszczacz ogólnego przeznaczenia potrzebuje jakiegoś sposobu, aby powiedzieć, co jest atomowe, a co można dalej podzielić. Ponadto nie jest oczywiste, w jaki sposób należy rozszerzyć algorytm, aby obejmował dane wejściowe o drzewiastych strukturach danych z danymi w węzłach, a także w liściach (przedpremierowe, po zamówieniu, przechodzenie wewnętrzne itp.)

Gareth Rees
źródło
Mówiąc wprost, oznacza to, że flattenfunkcję jednopoziomową można zdefiniować jako lambda z: [x for y in z for x in y].
Christopher Martin
1
„Spłaszczacz ogólnego przeznaczenia potrzebuje jakiegoś sposobu, aby powiedzieć, co jest atomowe, a co można dalej podzielić.”: Brzmi to jak problem, który można rozwiązać za pomocą OOP: każdy obiekt może mieć flattenmetodę. Implementacja tej metody powinna rekurencyjnie wywoływać flattenswój podskładnik, jeśli obiekt jest złożony. Niestety AFAIK nie każda wartość jest obiektem w Pythonie. W Ruby powinno to jednak działać.
Giorgio
1
spłaszczony pomocnik dla spłaszczenia o jeden poziom zamiast ciągłego „za w za” jest już wystarczająco dobrym przypadkiem IMO. łatwo czytelny
dtc
2
@Giorgio Python unika takich metod. Protokoły są preferowane i uważam, że ich praca jest znacznie płynniejsza niż projektowanie OOP, ponieważ często nie trzeba wcale implementować zbyt wiele.
jpmc26
8

Jest wyposażony w taką metodę, ale nie nazywa go spłaszczeniem. Nazywa się to „ łańcuchem ”. Zwraca iterator, którego należy użyć za pomocą funkcji list (), aby z powrotem przekształcić go w listę. Jeśli nie chcesz używać *, możesz użyć drugiej wersji „from_iterator”. Działa tak samo w Pythonie 3. Nie powiedzie się, jeśli lista wejściowa nie jest listą.

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

Kiedyś istniała metoda spłaszczenia w module compiler.ast, ale została ona przestarzała w wersji 2.6, a następnie usunięta w wersji 3.0. Dowolna rekurencja głębokości, niezbędna w przypadku dowolnie zagnieżdżonych list, nie działa dobrze z zachowawczą maksymalną głębokością rekurencji Pythona. Przyczyny usunięcia kompilatora były w dużej mierze spowodowane bałaganem . Kompilator został przekształcony w ast, ale spłaszczenie pozostało w tyle.

Głębokość arbitralną można osiągnąć za pomocą tablic numpy i spłaszczenia biblioteki.

Inżynier świata
źródło
chain.from_iteratorFunkcja, jak pan powiedział, może być używany tylko do spłaszczyć list dwuwymiarowe. Actualy spłaszczyć funkcja, która przyjmuje żadnych ilości zagnieżdżonych list i zwraca listę jednowymiarowy, nadal będzie masowo przydatna w wielu przypadkach (przynajmniej moim zdaniem)
Hubro
2
@Hubro: „w wielu przypadkach” - czy możesz wymienić sześć?
Gareth Rees,
1
@GarethRees: Podałem tutaj kilka przykładów: programmers.stackexchange.com/questions/254279/…
Hubro
Chciałbym również posunąć się do stwierdzenia, że ​​jeśli te inne języki faktycznie zapewniają taką funkcję spłaszczenia listy w bardzo prosty opisany sposób, jest to jeden z najbardziej przekonujących argumentów na poparcie dodania tej prostej zdolności do Pythona.
Bobort
Czy zwraca iterator lub generator?
jpmc26
-1

... może dlatego, że sam nie jest tak trudno napisać

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

... a następnie spłaszcz wszystko, co chcesz :)

>>> 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]
>>> 
Shreyas
źródło
8
pytający zdaje sobie z tego sprawę: „w Pythonie trzeba przebrnąć przez pisanie funkcji spłaszczania tablic od zera”. To nawet nie próbuje odpowiedzieć na zadane pytanie: „Wydaje mi się to głupie, spłaszczanie tablic jest tak powszechną rzeczą. To jak pisanie niestandardowej funkcji do łączenia dwóch tablic”.
komar
1
Nie na temat ... Ale super fajnie :-) !!
SeF
ta odpowiedź jest jak powiedzenie OP, że nie jest dobrym programistą, ponieważ nie wiedział, jak sam zakodować tę funkcję. Proponuję zmodyfikować początek odpowiedzi, ponieważ jest to przydatny kod dla tych, którzy natkną się na pytanie, nawet jeśli są nie na temat
Federico Bonelli