Понимание списка с вложенными циклами и условием if, а также членство в новом списке - PullRequest
0 голосов
/ 03 октября 2019

Вот мой обычный вложенный цикл с условием if и членством в новом списке:

wordlist = ["micro", "macro", "stats"]
letterlist = []

for aword in wordlist:
    for aletter in aword:
        if aletter not in letterlist:  
            letterlist.append(aletter)
print(letterlist)

, который печатает буквы без дубликатов: ['m', 'i', 'c', 'r', 'o', 'a', 's', 't']

Когда я пытаюсь сделать то же самое, используя понимание списка, я могу только пройти через вложенные циклы:

wordlist = ["micro", "macro", "stats"]
letterlist = [aletter for aword in wordlist for aletter in aword]
print(letterlist)

Это печатает все буквы с дубликатами: ['m', 'i', 'c', 'r', 'o', 'm', 'a', 'c', 'r', 'o', 's', 't', 'a', 't', 's']

К сожалению, это не работает:

wordlist = ["micro", "macro", "stats"]
letterlist = [[if aletter not in letterlist] for aword in wordlist for aletter in aword]

Вопрос: Как мне выполнить команду nestloop с оператором if, используя списковое понимание, основанное на моем примере выше?

Заранее спасибо

Ответы [ 6 ]

4 голосов
/ 03 октября 2019

Вы можете использовать функции dict.fromkeys() и chain.from_iterable():

from itertools import chain

list(dict.fromkeys(chain.from_iterable(wordlist)))
# ['m', 'i', 'c', 'r', 'o', 'a', 's', 't']

В Python 3.6 и ниже вам необходимо заменить dict на OrderedDict.

3 голосов
/ 03 октября 2019

Нет. Вы не можете сделать это, используя понимание списка, потому что вам нужно создать список букв, которые были замечены. Я считаю, что ваш лучший способ действий - это использовать цикл for. Если вам нужно сохранить порядок букв, используйте как список, так и набор (список для поддержания порядка, набор для проверки членства O (1) для каждой буквы). Если порядок не имеет значения, тогда просто используйте заданное понимание, то есть {letter for word in word_list for letter in word}

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

word_list = ["micro", "macro", "stats"]
letter_list = []
letters_seen = set()

for word in word_list:
    for letter in word:
        if letter in letters_seen:
            continue
        letters_seen.add(letter)
        letter_list.append(letter)

>>> letter_list
['m', 'i', 'c', 'r', 'o', 'a', 's', 't']

Время

wordlist = ["micro", "macro", "stats"] * 100_000

%%timeit
res=[]
[res.append(aletter) for aword in wordlist for aletter in aword if aletter not in res]
# 174 ms ± 8.37 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
letter_list = []
letters_seen = set()

for word in wordlist:
    for letter in word:
        if letter in letters_seen:
            continue
        letters_seen.add(letter)
        letter_list.append(letter)
# 71.1 ms ± 1.15 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit list(dict.fromkeys(''.join(wordlist)))
# 37.1 ms ± 1.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit list(dict.fromkeys(chain.from_iterable(wordlist)))
# 46.8 ms ± 2.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# Slightly slower, but requires less memory to run.

# Baseline comparison if order is not important (i.e. use sets).
%timeit {letter for word in wordlist for letter in word}
# 88.8 ms ± 6.48 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
2 голосов
/ 03 октября 2019

вы можете сделать это следующим образом

from collections import OrderedDict

wordlist = ["micro", "macro", "stats"]    
sol = list(OrderedDict.fromkeys(''.join(wordlist)).keys())    
print(sol)

вывод

['m', 'i', 'c', 'r', 'o', 'a', 's', 't']

вы также можете использовать

sol =  [*OrderedDict.fromkeys(''.join(wordlist)).keys()]

с помощью dict это можно сделатькак

  sol = list(dict((i,1) for i in ''.join(wordlist)).keys())

Добавление решения @alexander здесь

sol = list(dict.fromkeys(''.join(wordlist)))    
1 голос
/ 03 октября 2019

Вы можете использовать Set понимание следующим образом:

letterlist = { aletter for aword in wordlist for aletter in aword}

Set по умолчанию не добавляет повторяющиеся значения. И это намного компактнее.

Стоит отметить, что оператор in имеет линейную сложность по времени при использовании на Lists, тогда как для Sets он имеет почти постоянную сложность по времени.

1 голос
/ 03 октября 2019

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

wordlist = ["micro", "macro", "stats"]
res=[]
[res.append(aletter) for aword in wordlist for aletter in aword if aletter not in res]
print(res)

ИЛИ

list(set([aletter for aword in wordlist for aletter in aword]))

Надеюсь, это поможет!

0 голосов
/ 03 октября 2019

Другое решение, просто добавление 2 строк кода к вашему собственному коду. Вы преобразуете свой список в словарь, по определению он принимает уникальные значения и снова в список (если он вам нужен как список)

for aword in wordlist:
   for aletter in aword:
       if aletter not in letterlist:
           letterlist.append(aletter)
       letterdict = list(dict.fromkeys(letterlist)) #list to dictionary
       letterlist = list(letterdict)
...