Почему в 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
«Я имею в виду, представьте, если вы внесете в свой код ошибку, которая непреднамеренно изменит структуру ваших данных. Flatten все равно будет работать, но даст совершенно неправильные результаты». Это одна из причин, почему мне нравятся языки со статической типизацией. ;-)
Джорджио


2
@BryanOakley См. Также предыдущий комментарий (хотя не для многоуровневых списков, сглаживание в общем является обычным явлением)
Izkata

3
Он встроен в Mathemaica, и я широко его использую.
За Александерссон

Ответы:


34

Предложения по добавлению flattenфункции в стандартную библиотеку время от времени появляются в списках рассылки python-dev и python-ideas . Разработчики Python обычно отвечают следующими моментами:

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

  2. Каковы варианты использования универсального многоуровневого выравнивания? Действительно ли они достаточно убедительны для добавления функции в стандартную библиотеку?

  3. Как бы универсальное многоуровневое выравнивание решало, когда выравнивать, а когда оставить в покое? Вы можете подумать, что будет работать правило типа «сплющить все, что поддерживает итеративный интерфейс», но это приведет к бесконечному циклу for flatten('a').

Смотрите, например, Раймонд Хеттингер :

Это обсуждалось до тошноты на comp.lang.python. Кажется, людям нравится писать свои собственные версии больше, чем находить законные варианты использования, у которых еще нет тривиальных решений.

Стабилизатору общего назначения нужен какой-то способ сказать, что является атомным, а что можно разделить далее. Кроме того, не очевидно, как алгоритм должен быть расширен для охвата входных данных древовидными структурами данных с данными в узлах, а также с конечными точками (предварительный порядок, почтовый порядок, обратный порядок в порядке следования и т. Д.)


Просто чтобы быть явным, это означает, что одноуровневая flattenфункция может быть определена как lambda z: [x for y in z for x in y].
Кристофер Мартин

1
«Универсальному сглаживающему устройству нужен какой-то способ сказать, что является атомарным, а что можно разделить на части». Это звучит как проблема, которую можно решить с помощью ООП: у каждого объекта может быть flattenметод. Реализация этого метода должна рекурсивно вызывать flattenего подкомпонент, если объект является составным. К сожалению, AFAIK не каждое значение является объектом в Python. В Ruby это должно работать, хотя.
Джорджио

1
помощник по сглаживанию для одноуровневого сглаживания, а не продолжительное «для в для» уже достаточно хороший случай ИМО. легко читаемый
dtc

2
@ Джорджио Питон уклоняется от таких методов. Протоколы предпочтительнее, и я считаю, что с ними работать гораздо проще, чем с ООП, так как зачастую вам даже не нужно вообще ничего реализовывать.
jpmc26

8

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

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

Был одно время Flatten метода в модуле compiler.ast , но это было устаревшим в 2.6 , а затем удален в 3.0. Произвольная рекурсия глубины, необходимая для произвольно вложенных списков, плохо работает с консервативной максимальной глубиной рекурсии Python. Причины для удаления компилятора были в значительной степени из-за того, что это беспорядок . Компилятор превратился в ast, но flatten остался позади.

Произвольная глубина может быть достигнута с помощью массивов 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
Аскер знает об этом: «в Python приходится сталкиваться с трудностями при написании функции для выравнивания массивов с нуля». Это даже не попытка ответить на заданный вопрос: «Это кажется мне глупым, сглаживание массивов - это обычное дело. Это похоже на необходимость написания пользовательской функции для объединения двух массивов».
комнат

1
Не по теме ... Но супер круто :-) !!
Sef

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