Разделить и сгладить список значений Strings и None, используя понимание - PullRequest
2 голосов
/ 29 февраля 2020

Учитывая список, который содержит как строки, так и значения None, в которые некоторые строки имеют встроенные символы новой строки, я буду sh разбивать строки с символами новой строки на несколько строк и возвращать плоский список.

Я написал код, чтобы сделать это, используя функцию генератора, но код довольно громоздкий, и мне интересно, возможно ли сделать это более кратко, используя понимание списка или функцию из модуля itertools . itertools.chain, похоже, не может отказаться от итерации любых не повторяемых элементов.

def expand_newlines(lines):
    r"""Split strings with newlines into multiple strings.

    >>> l = ["1\n2\n3", None, "4\n5\n6"]
    >>> list(expand_newlines(l))
    ['1', '2', '3', None, '4', '5', '6']
    """
    for line in lines:
        if line is None:
            yield line
        else:
            for l in line.split('\n'):
                yield l

Ответы [ 6 ]

3 голосов
/ 29 февраля 2020

Вы можете использовать yield from.

def expand(lines):
    for line in lines:
        if isinstance(line,str):
            yield from line.split('\n')
        elif line is None:
            yield line

list(expand(l))
#['1', '2', '3', None, '4', '5', '6']
2 голосов
/ 29 февраля 2020

Вот одна строка, но я думаю, что решение @ Ch3steR более читабельно.

from itertools import chain

list(chain.from_iterable(i.splitlines() if i is not None and '\n' in i else [i] 
                         for i in lines))
1 голос
/ 06 марта 2020

Использование more_itertools.collapse для выравнивания вложенных списков:

Дано

import more_itertools as mit


lst = ["1\n2\n3", None, "7\n8\n9"]

Демонстрация

list(mit.collapse([x.split("\n") if x else x for x in lst ]))

# ['1', '2', '3', None, '7', '8', '9']

more_itertools - сторонний пакет. Установить через > pip install more_itertools.

1 голос
/ 29 февраля 2020

Вы можете использовать itertools.chain, если выполните следующее

import itertools

def expand_newlines(lines):

    return itertools.chain.from_iterable(x.split("\n") if x else [None]
                                         for x in lines)
0 голосов
/ 02 марта 2020

Аналогично ответу @ blueteeth, но более кратким путем обращения логики c:

import itertools
chainfi = itertools.chain.from_iterable

def expand_newlines(lines):
    r"""Split strings with newlines into multiple strings.

    >>> l = ["1\n2\n3", None, "4\n5\n6"]
    >>> list(expand_newlines(l))
    ['1', '2', '3', None, '4', '5', '6']
    """
    return chainfi([None] if l is None else l.split('\n') for l in lines)

None - это особый случай, поэтому мы должны проверять это.

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

0 голосов
/ 29 февраля 2020

Если вы можете изменить список на месте, то вы можете сделать:

lst = ["1\n2\n3", None, "4\n5\n6"]
for i in range(len(lst))[::-1]:
    if isinstance(lst[i], str):
        lst[i:i+1] = lst[i].split("\n")
print(lst)  # ['1', '2', '3', None, '4', '5', '6']

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

...