Як перевірити, чи всі елементи списку відповідають умові?


208

У мене є список, який складається приблизно з 20000 списків. Я використовую 3-й елемент кожного списку як прапор. Я хочу виконати деякі операції в цьому списку до тих пір, поки щонайменше прапор одного елемента дорівнює 0, це так:

my_list = [["a", "b", 0], ["c", "d", 0], ["e", "f", 0], .....]

На початку всі прапори дорівнюють 0. Я використовую цикл час, щоб перевірити, чи хоча б прапор одного елемента дорівнює 0:

def check(list_):
    for item in list_:
        if item[2] == 0:
            return True
    return False

Якщо check(my_list)повернеться True, я продовжую працювати над своїм списком:

while check(my_list):
    for item in my_list:
        if condition:
            item[2] = 1
        else:
            do_sth()

Насправді я хотів видалити елемент з my_list, коли я повторював його, але мені не дозволяється видаляти елементи, оскільки я повторюю його.

Оригінальний мій список не мав прапорів:

my_list = [["a", "b"], ["c", "d"], ["e", "f"], .....]

Оскільки я не міг видалити елементи під час повторення, я вигадав ці прапори. Але він my_listмістить багато елементів, і whileцикл читає їх у кожному forциклі, і це забирає багато часу! Чи є у вас якісь пропозиції?


3
Схоже, ваша структура даних не ідеальна для вашої проблеми. Якщо ви трохи більше пояснили контекст, можливо, ми могли б запропонувати щось більш відповідне.
— uselpa

Можливо, ви могли б замінити елементи на Noneабо []під час перегляду списку замість того, щоб видаляти їх. Перевірка всього списку за допомогою повторення "check ()" над усіма елементами перед кожним проходом у внутрішній цикл є дуже повільним підходом.
— мартіно

Відповіді:


402

Найкращою відповіддю тут є використання all(), яке є основою для даної ситуації. Ми поєднуємо це з генераторним виразом, щоб отримати результат, який ви бажаєте, чисто та ефективно. Наприклад:

>>> items = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
True
>>> items = [[1, 2, 0], [1, 2, 1], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
False

Зауважте, що all(flag == 0 for (_, _, flag) in items)це прямо рівнозначно all(item[2] == 0 for item in items), в цьому випадку читати просто трохи приємніше.

І, на прикладі фільтра, розуміння списку (звичайно, ви можете використовувати вираз генератора, де це доречно):

>>> [x for x in items if x[2] == 0]
[[1, 2, 0], [1, 2, 0]]

Якщо ви хочете перевірити хоча б один елемент 0, кращим варіантом є використання, any()який читається:

>>> any(flag == 0 for (_, _, flag) in items)
True

Моя провина у використанні лямбда, Python не сприймає функцію як перший аргумент, як Haskell et. ін., я змінив свою відповідь і на розуміння списку. :)
— Гампус Нільссон

3
@HampusNilsson Розуміння списку не є таким, як вираз генератора. Як all()і any()коротке замикання, якщо, наприклад, перше значення в шахті оцінюється на False, all()не вдасться і більше не перевіряє значення, повертаючись False. Ваш приклад зробить те саме, за винятком того, що спочатку буде генеруватися весь список порівнянь, що означає багато обробки за безцінь.
— Гарет Летті

14

Якщо ви хочете перевірити, чи будь-який елемент у списку порушує умову, використовуйте all:

if all([x[2] == 0 for x in lista]):
    # Will run if all elements in the list has x[2] = 0 (use not to invert if necessary)

Щоб видалити всі невідповідні елементи, використовуйте filter

# Will remove all elements where x[2] is 0
listb = filter(lambda x: x[2] != 0, listb)

2
Ви можете видалити [...]в , all(...)так як це може створити генератор замість списку, який не тільки економить вам два символу , а й економить пам'ять і час. За допомогою генераторів одночасно буде обчислюватися лише один елемент (колишні результати знижуються, оскільки більше не використовуються), і якщо будь-який з них виявиться False, генератор припинить обчислювати решту.
— InQβ

7

Ви можете використовувати itertools так, як це, воно припиняється, коли буде виконано умову, яка не відповідає вашій заяві. Протилежний метод був би краплинним

for x in itertools.takewhile(lambda x: x[2] == 0, list)
    print x

0

Ще один спосіб використання itertools.ifilter. Це перевіряє правдивість та процес (використовуючи lambda)

Зразок-

for x in itertools.ifilter(lambda x: x[2] == 0, my_list):
    print x

0

цей спосіб трохи гнучкіший, ніж використання all():

my_list = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
all_zeros = False if False in [x[2] == 0 for x in my_list] else True
any_zeros = True if True in [x[2] == 0 for x in my_list] else False

або більш лаконічно:

all_zeros = not False in [x[2] == 0 for x in my_list]
any_zeros = 0 in [x[2] for x in my_list]

Чи не могли ви просто сказати all_zeros = False in [x[2] == 0 for x in my_list]чи навіть 0 in [x[2] for x in my_list]і відповідно any_zeros? Я справді не бачу помітного поліпшення all().
— tripleee

ні, ваша версія - all_zeros = False in [x[2] == 0 for x in my_list]оцінює до False, а моя оцінює до True. Якщо змінити його, all_zeros = not (False in [x[2] == 0 for x in my_list])то він еквівалентний моєму. І 0 in [x[2] for x in my_list]очевидно лише збирається працювати any_zeros. Але мені подобається лаконічність вашої ідеї, тому я
— оновлю
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.