Чому Python не має функції «сплющити» для списків?


39

Erlang і Ruby оснащені функціями для вирівнювання масивів. Здається, такий простий і корисний інструмент для додавання до мови. Можна зробити так:

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

Або навіть:

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

Натомість, у Python доводиться переживати проблеми написання функції для вирівнювання масивів з нуля. Мені це здається дурним, згладжувати масиви - це така звичайна річ. Це як написати необхідну функцію для об'єднання двох масивів.

Я це безрезультатно гуглю, тому прошу тут; Чи є конкретна причина, чому така зріла мова, як Python 3, що постачається зі ста тисячами різних батарей, не пропонує простий метод згладжування масивів? Чи обговорювалась і відкидалася ідея включення такої функції в якийсь момент?


2
@detly: Нещодавно мені траплялося сплощення, коли я використовував кілька запитів для отримання даних з різних джерел. Кожен запит повертає список словників, тому в підсумку у мене є список списків словників, які слід перетворити на список словників. Я використав петлю +, extendале вирівняти було б набагато елегантніше. Однак я думаю, якщо ця закономірність є досить поширеною, щоб виправдати те, що сплющується в стандартній бібліотеці.
Джорджіо

4
"Я маю на увазі, уявіть собі, якщо ви введете помилку у свій код, яка ненавмисно змінює структуру ваших даних. Вирівнювання все одно буде працювати, але дасть абсолютно неправильні результати". ;-)
Джорджіо


2
@BryanOakley Дивіться також попередній коментар (хоча це не для багаторівневих списків, сплощення загалом є загальним)
Izkata

3
Він вбудований в Mathemaica, і я його широко використовую.
Пер Александерсон

Відповіді:


34

Пропозиції щодо flattenфункції, яку потрібно додати до стандартної бібліотеки, час від часу з’являються у списках розсилки python-dev та python-ідеї . Розробники Python зазвичай відповідають на такі моменти:

  1. Однорівневий вирівнювання (перетворення ітерабелів ітерабелів в єдиний ітерабельний) - це тривіальне однорядкове вираження (x for y in z for x in y)і в будь-якому випадку вже знаходиться в стандартній бібліотеці під назвою itertools.chain.from_iterable.

  2. Які випадки використання для багаторівневого вирівнювання загального призначення? Чи дійсно вони є такими переконливими, щоб функція була додана до стандартної бібліотеки?

  3. Як би багаторівневе багаторівневе вирівнювання вирішило, коли вирівняти і коли залишити самоту? Ви можете подумати, що таке правило, як "вирівняти все, що підтримує ітерабельний інтерфейс", буде працювати, але це призведе до нескінченного циклу для flatten('a').

Дивіться, наприклад, Реймонд Хеттінгер :

Це було обговорено рекламною нудотою на comp.lang.python. Людям, здається, подобається писати власні версії згладжувати більше, ніж знаходити законні випадки використання, які ще не мають тривіальних рішень.

Плоскогубці загального призначення потребує певного способу пояснення, що є атомним і що можна далі підрозділити. Крім того, не очевидно, як алгоритм повинен бути розширений, щоб охоплювати входи з деревоподібними структурами даних з даними по вузлах, а також листям (попереднє замовлення, порядок замовлення, проходження запиту тощо).


Якщо бути явним, це означає, що однорівнева flattenфункція може бути визначена як lambda z: [x for y in z for x in y].
Крістофер Мартін

1
"Плоскогубці загального призначення потребує певного способу пояснення, що є атомним і що можна далі розділити.": Це звучить як проблема, яку можна вирішити за допомогою OOP: кожен об'єкт може мати flattenметод. Реалізація цього методу повинна рекурсивно закликати flattenйого підкомпонент, якщо об'єкт є складовим. На жаль, AFAIK не кожне значення є об'єктом у Python. У Рубі це все-таки має працювати.
Джорджіо

1
вирівняти помічник для однорівневого вирівнювання, а не для продовження «за в» в - вже досить хороший випадок ІМО. легко читабельна
dtc

2
@Giorgio Python ухиляється від таких методів. Протоколи є кращими, і я вважаю, що з ними набагато легше працювати, ніж з дизайном OOP, оскільки вам часто навіть не потрібно дуже багато впроваджувати.
jpmc26

8

Він є таким методом, але він не називає його вирівнюванням. Це називається " ланцюжок ". Він повертає ітератор, який вам потім потрібно буде використовувати функцію list (), щоб повернути його до списку. Якщо ви не хочете використовувати *, ви можете використовувати другу "from_iterator" версію. Це працює так само в Python 3. Він не вдасться, якщо введення списку не є списком списків.

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

Свого часу у модулі compiler.ast був метод вирівнювання, але це було застаріло в 2.6, а потім видалено в 3.0. Довільна рекурсія глибини, необхідна для довільно вкладених списків, не працює добре при максимальній глибині рекурсії Python's. Причини для видалення компілятора багато в чому були пов'язані з тим, що це було безладом . Компілятор перетворився на аст, але згладжений залишився позаду.

Довільну глибину можна досягти за допомогою масивів numpy та згладити цю бібліотеку.


Як chain.from_iteratorви вже говорили, функцію можна використовувати лише для вирівнювання двовимірних списків. Actualy розплющити функція, яка приймає будь-які суми вкладених списків і повертає одновимірний списку, все одно буде масово корисно в багатьох випадках ( по крайней мере , на мій погляд)
Hubro

2
@Hubro: "у багатьох випадках" - можна назвати шість?
Гарет Різ

1
@GarethRees: Я дав кілька прикладів тут: programmers.stackexchange.com/questions/254279 / ...
Hubro

Я б також пішов так далеко, щоб стверджувати, що якщо ці інші мови насправді надають таку функцію, щоб вирівняти список дуже простим описаним способом, це є одним із найбільш переконливих аргументів на підтримку додавання цієї простої здатності до Python.
Бобборт

Чи повертає він ітератор чи генератор?
jpmc26

-1

... можливо, тому що не так складно написати один

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

... а потім згладьте все, що завгодно :)

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

8
Asker знає про це: "у Python потрібно пройти через проблему написання функції для вирівнювання масивів з нуля". Це навіть не намагається вирішити поставлене питання: "Мені це здається нерозумним, згладжування масивів - це така звичайна річ. Це як написати необхідну функцію для об'єднання двох масивів".
гнат

1
Поза темою ... Але супер круто :-) !!
СеФ

ця відповідь - це як сказати ОП, що він не хороший розробник, тому що він не знав, як сам кодувати функцію. Я пропоную вам змінити початок своєї відповіді, оскільки це корисний код для тих, хто натрапляє на питання, навіть якщо це поза темою
Федеріко Бонеллі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.