Сгладить список строк и списков строк и списков в Python
Аналогичные вопросы задавали раньше, но решения для них не работают для моего варианта использования (например, Создание плоского списка из списка списков в Python и Сглаживание мелкого списка в Python. У меня есть список строк и списков, где встроенный список также может содержать строки и списки. Я хочу превратить это в простой список строк без разбиения строк на список символов.
import itertools
list_of_menuitems = ['image10', ['image00', 'image01'], ['image02', ['image03', 'image04']]]
chain = itertools.chain(*list_of_menuitems)
Итоговый список:
['i', 'm', 'a', 'g', 'e', '1', '0', 'image00', 'image01', 'image02', ['image03', 'image04']]
Ожидаемый результат:
['image10', 'image00', 'image01', 'image02', 'image03', 'image04']
Какой лучший (Pythonic) способ сделать это?
Ответы
Ответ 1
Часто повторяющаяся функция flatten
может быть применена к этому обстоятельству с простой модификацией.
from collections import Iterable
def flatten(coll):
for i in coll:
if isinstance(i, Iterable) and not isinstance(i, basestring):
for subc in flatten(i):
yield subc
else:
yield i
basestring
будет гарантировать, что объекты str
и unicode
не будут разделены.
Существуют также версии, которые рассчитываются на i
, не имеющих атрибута __iter__
. Я не знаю обо всем этом, потому что я думаю, что str
теперь имеет этот атрибут. Но, стоит упомянуть.
(Пожалуйста, поддержите связанный ответ.)
Ответ 2
Использование рекурсии.
def flatten(A):
rt = []
for i in A:
if isinstance(i,list): rt.extend(flattern(i))
else: rt.append(i)
return rt
Тестовое задание:
>>> list_of_menuitems = ['image10', ['image00', 'image01'], ['image02', ['image0
3', 'image04']]]
>>> flattern(list_of_menuitems)
['image10', 'image00', 'image01', 'image02', 'image03', 'image04']
Ответ 3
Следующие строки для строк (и будут легко адаптированы к другим типам):
def flatten_to_strings(listOfLists):
"""Flatten a list of (lists of (lists of strings)) for any level
of nesting"""
result = []
for i in listOfLists:
# Only append if i is a basestring (superclass of string)
if isinstance(i, basestring):
result.append(i)
# Otherwise call this function recursively
else:
result.extend(flatten_to_strings(i))
return result
flatten_to_strings(list_of_menuitems)
Out[2]: ['image10', 'image00', 'image01', 'image02', 'image03', 'image04']
Ответ 4
В одном специализированном случае, когда ни один из элементов списка не содержит один из следующих разделителей []'
, вы можете использовать следующий хак. Я не профилировал его, но очевидно, что это будет иметь лучшую производительность, чем очевидное и более чистое рекурсивное решение.
>>> str(list_of_menuitems).translate(None,"[]'").split(',')
['image10', ' image00', ' image01', ' image02', ' image03', ' image04']
Я согласен, это грязный взлом, но выполняет JOB без особых усилий.
Ответ 5
Это общий рекурсивный сплюс, который может использоваться для работы с любой комбинацией типов, которые должны или не должны быть сплющены:
import collections
def generic_flatten(seq, flatten_types=(tuple,list,set),atom_types=(basestring,dict),fixtype=True):
newseq = []
for item in seq:
if (not isinstance(collections.Iterable)) or any(isinstance(i,t) for t in atom_types):
newseq.append(item)
elif any(isinstance(i,t) for t in flatten_types): # set flatten_types to (object,) or (collections.Iterable,) to disable check
newseq.extend(generic_flatten(item, flatten_types, atom_types,fixtype)
if fixtype and type(newseq) is not type(seq):
newseq = type(seq)(newseq)
return newseq
yield
и chain
могут использоваться для создания общей версии, основанной на итераторе.