Какой самый быстрый способ сгладить произвольно вложенные списки в Python?

Возможный дубликат:
Сглаживание мелкого списка в Python
Свести (нерегулярный) список списков в Python

РЕДАКТИРОВАТЬ: Вопрос не в том, как это сделать - это обсуждалось в других вопросах - вопрос в том, какой метод самый быстрый?

Я уже нашел решения, но мне интересно, какое самое быстрое решение - сгладить списки, которые содержат другие списки произвольной длины.

Например:

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

Станет:

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

Там может быть бесконечно много уровней. Некоторые из объектов списка могут быть строками, которые не должны быть сведены в последовательные символы в списке вывода.

3 ответа

Решение

Вот рекурсивный подход, дружественный к строкам:

nests = [1, 2, [3, 4, [5],['hi']], [6, [[[7, 'hello']]]]]

def flatten(container):
    for i in container:
        if isinstance(i, (list,tuple)):
            for j in flatten(i):
                yield j
        else:
            yield i

print list(flatten(nests))

возвращает:

[1, 2, 3, 4, 5, 'hi', 6, 7, 'hello']

Обратите внимание, что это не дает никаких гарантий для скорости или накладных расходов, но иллюстрирует рекурсивное решение, которое, будем надеяться, будет полезным.

Это не должно быть рекурсивным. На самом деле, итеративное решение часто быстрее из-за накладных расходов, связанных с вызовами функций. Вот итерационная версия, которую я написал некоторое время назад:

def flatten(items, seqtypes=(list, tuple)):
    for i, x in enumerate(items):
        while i < len(items) and isinstance(items[i], seqtypes):
            items[i:i+1] = items[i]
    return items

Не тестировали производительность этой конкретной реализации, но она, вероятно, не так хороша из-за всех назначений срезов, которые могут привести к перемещению большого количества памяти. Тем не менее, не думайте, что он должен быть рекурсивным, или что так проще написать.

Эта реализация имеет преимущество, заключающееся в том, что список выравнивается "на месте", а не возвращается копия, как это делают рекурсивные решения. Это может быть полезно, когда память ограничена. Если вам нужна плоская копия, просто передайте мелкую копию списка, который вы хотите сгладить:

flatten(mylist)                # flattens existing list
newlist = flatten(mylist[:])   # makes a flattened copy

Кроме того, этот алгоритм не ограничен пределом рекурсии Python, потому что он не рекурсивен. Однако я уверен, что это практически никогда не вступит в игру.

Эта функция должна иметь возможность быстро выравнивать вложенные итеративные контейнеры без использования рекурсии:

import collections

def flatten(iterable):
    iterator = iter(iterable)
    array, stack = collections.deque(), collections.deque()
    while True:
        try:
            value = next(iterator)
        except StopIteration:
            if not stack:
                return tuple(array)
            iterator = stack.pop()
        else:
            if not isinstance(value, str) \
               and isinstance(value, collections.Iterable):
                stack.append(iterator)
                iterator = iter(value)
            else:
                array.append(value)

Примерно через пять лет мое мнение по этому вопросу изменилось, и это может быть даже лучше использовать:

def main():
    data = [1, 2, [3, 4, [5], []], [6]]
    print(list(flatten(data)))


def flatten(iterable):
    iterator, sentinel, stack = iter(iterable), object(), []
    while True:
        value = next(iterator, sentinel)
        if value is sentinel:
            if not stack:
                break
            iterator = stack.pop()
        elif isinstance(value, str):
            yield value
        else:
            try:
                new_iterator = iter(value)
            except TypeError:
                yield value
            else:
                stack.append(iterator)
                iterator = new_iterator


if __name__ == '__main__':
    main()
Другие вопросы по тегам