Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 15 из 20 Начальный 15 мин 100 XP

any и all в Python: проверка без ручного цикла

any и all отвечают на вопросы «есть ли хоть один» и «все ли до единого» одной строкой — вместо цикла с булевым флагом.

Редакция Питоники

Задачи «есть ли в списке хоть один плохой элемент» и «все ли элементы хорошие» встречаются постоянно: есть ли двойки в журнале, все ли поля формы заполнены, остались ли товары на складе. До сих пор ответ искали циклом с булевым флагом: завести переменную, менять её в цикле, проверить после. Python умеет отвечать на оба вопроса одной функцией: any — «хотя бы один», all — «все до единого». Функция any возвращает True, если условие выполнилось хотя бы для одного элемента, а all — если для всех без исключения.

any: хотя бы один

Сравним оба подхода на журнале оценок: нужно узнать, есть ли в нём двойки.

цикл с флагом против any
marks = [4, 2, 5, 3]

has_bad = False
for m in marks:
    if m == 2:
        has_bad = True
        break
print(has_bad)

print(any(m == 2 for m in marks))
Вывод
True
True

Сверху — ручной способ, который мы писали в уроках про break и аккумуляторы: флаг has_bad стартует как False, цикл ходит по оценкам, переворачивает флаг, найдя двойку, и останавливается — дальше искать бессмысленно. Снизу — та же проверка одной строкой. any(...) принимает последовательность значений True и False и отвечает True, если среди них есть хотя бы один True. Внутрь скобок передана заготовка m == 2 for m in marks — генератор, который выдаёт результат сравнения для каждой оценки; генераторы списков — близкий родственник, разница только в квадратных скобках.

any на готовых значениях
print(any([0, 0, 5]))
print(any([0, '', []]))
print(any([False, False]))
Вывод
True
False
False

any годится и для готовых значений, без условия: непустая строка, ненулевое число и непустой список истинны, нули и пустые значения ложны. Поэтому any([0, '', []]) вернул False: среди значений нет ни одного истинного. Но чаще интересует не «есть ли хоть одно ненулевое», а «выполнено ли условие» — тогда внутрь передают генератор с условием, как в примере с оценками.

all: все до единого

all работает зеркально: True только если истинны все элементы сразу.

all по условию
marks = [4, 5, 4, 5]

print(all(m >= 3 for m in marks))

marks = [4, 5, 2, 5]
print(all(m >= 3 for m in marks))
Вывод
True
False

Вопрос одинаковый — все ли оценки не хуже тройки, — а ответы разные. Первый журнал целиком проходит проверку. Во втором живёт двойка, и одного нарушителя достаточно: правило «все до единого» ломается первым же неподходящим элементом, остальные можно даже не смотреть.

пустая последовательность
print(any([]))
print(all([]))
Вывод
False
True

Пустая последовательность — каверзный случай, и его стоит просто запомнить. any([]) отвечает False: подходящих не нашли. all([]) отвечает True — и это логично, если читать all как «не найдено ни одного нарушителя»: в пустом списке нарушителей нет, проверка пройдена. Поэтому перед all на пустых данных стоит лишний раз подумать: True на пустом списке может оказаться не тем ответом, которого ждёт программа.

any и all на практике

Соберём две живые проверки. Первая — анкета: все ли ответили «да». Вторая — форма регистрации: заполнены ли все поля и есть ли в форме телефон.

анкета: any и all вместе
answers = ['да', 'нет', 'да']

print(any(a == 'да' for a in answers))
print(all(a == 'да' for a in answers))
Вывод
True
False

Хотя бы один согласился — any ответил True. Все согласились — нет, есть один «нет», и all ответил False. Два вопроса, две функции, ноль циклов с флагами.

форма регистрации
form = {'name': 'Анна', 'email': '', 'city': 'Москва'}

filled = all(v != '' for v in form.values())
print('Все поля заполнены:', filled)

has_phone = any(k == 'phone' for k in form)
print('Есть телефон:', has_phone)
Вывод
Все поля заполнены: False
Есть телефон: False

Обход словаря по values() отдаёт значения полей, и all проверяет, что пустых среди них нет: email ввели пустой строкой, форма не готова к отправке. Обход по ключам нашёл бы телефон через any — но такого поля в форме нет, и ответ False. Оба ответа собраны без единого ручного цикла: их пишут функции.

короткое замыкание
def check(n):
    print('проверяем', n)
    return n > 2

print(any(check(n) for n in [3, 5, 1]))
Вывод
проверяем 3
True

Вывод показывает главное устройство any: проверена только тройка, а пятёрка и единица не трогались. Как только генератор выдал первый True, функция остановилась — ответ уже ясен. Это называется коротким замыканием, и оно работает в обе стороны: any останавливается на первом True, all — на первом False. На длинных списках это заметная экономия: до половины данных проверка может не дойти.

any и all на словаре

Словарь проверяется так же, как список, только обход выбирают осознанно: values() отдаёт сами значения, keys() — ключи. Для проверки «есть ли дорогая позиция» нужен обход по значениям, а для «есть ли поле с таким именем» — по ключам.

проверка цен и имён
prices = {'хлеб': 50, 'сыр': 300, 'молоко': 80}

print(any(p > 200 for p in prices.values()))
print(all(p > 0 for p in prices.values()))
Вывод
True
True

Первая строка — «есть ли что-то дороже двухсот»: сыр стоит 300, ответ True. Вторая — «все ли цены положительные»: пустых и отрицательных нет, ответ True. Одна и та же функция, два разных вопроса к тем же данным.

проверка по ключам
form = {'name': 'Анна', 'email': '', 'city': 'Москва'}

print(any(k == 'phone' for k in form))
print(all(form.values()))
Вывод
False
False

Телефона в форме нет — первый ответ False. Второй проверяет значения, и пустая строка email оказалась ложной, поэтому all ответил False. Покажите any на значениях — и вы получите True: пустая строка не мешает, если нужен хоть один непустой элемент. Одна и та же форма, два разных вопроса к ней.

Сколько подходящих: sum по булевым значениям

any и all отвечают на вопросы «есть хоть один» и «все до единого». Третий вопрос — «а сколько именно» — они не решают, но решает их ближайший родственник: встроенная sum. В Python логические значения(True и False) — это числа 1 и 0.

считаем проходы
flags = [True, False, True]

print(sum(flags))
print(sum(1 for f in flags if f))
Вывод
2
2

Два ответа совпали: True посчитали как единицу, False — как ноль. Второй вариант с генератором даёт то же самое, но работает и с не-булевыми значениями: если в списке лежат числа или строки, sum напрямую сложит их и получит ерунду, а sum(1 for ...) посчитает только успешные проверки. Практический вывод: когда нужен ответ «сколько прошло», sum(1 for ...) надёжнее, чем sum по сырым значениям.

Сложные условия: вложенные проверки

Условие внутри генератора может быть сколь угодно сложным, и any с ним справляется. Проверить «есть ли нечётное, которое больше пяти» — одна строка, где внутри условия стоит and.

два условия в одном
nums = [3, 7, 9]

print(any(n % 2 == 0 for n in nums))
print(all(n % 2 == 1 for n in nums))
print(any(n % 2 == 1 and n > 5 for n in nums))
Вывод
False
True
True

Первая строка: чётных нет — False. Вторая: все три нечётные — True. Третья: нечётное больше пяти есть (семь и девять) — True. Обратите внимание на третью строку: условие с and проверяется для каждого элемента целиком, и any остановится на первом подходящем — на семёрке, не дойдя до девятки.

any по вложенным структурам
matrix = [[1, 2], [0, 4]]

print(all(all(row) for row in matrix))
print(any(all(row) for row in matrix))
Вывод
False
True

Вложенный any: для каждой строки спрашиваем all — все ли её числа ненулевые. Ответ True есть (строка [1, 2] целиком ненулевая). А вот all(all(row) ...) — «все строки целиком ненулевые» — даёт False: во второй строке стоит ноль, который Python считает ложным. Единица истинна, а ноль — нет, хотя оба это числа.

Замена флага: три поколения кода

Один и тот же вопрос «есть ли плохой элемент» в разное время писался по-разному. Первое поколение — цикл с флагом: завели переменную, меняли её в теле, проверяли после. Второе — цикл с флагом и break: то же, но с досрочным выходом. Третье — одна строка с any.

три записи одного вопроса
marks = [4, 2, 5, 3]

has_bad = False
for m in marks:
    if m == 2:
        has_bad = True
        break

print('вручную:', has_bad)
print('через any:', any(m == 2 for m in marks))
Вывод
вручную: True
через any: True

Два ответа совпадают, но видны два разных уровня. Цикл с флагом — это пять строк ради одного слова True или False; any делает то же самое в одну. Если вы пишете цикл и в конце проверяете булеву переменную, задайте себе вопрос: что мешает написать это одной строкой? Обычно ответа нет.

all вместо двух флагов
marks = [4, 5, 2, 5]

ok = True
for m in marks:
    if m < 3:
        ok = False

print(ok)
print(all(m >= 3 for m in marks))
Вывод
False
False

Здесь разница видна в самом коде. Ручной вариант обходит весь список и на каждой плохой оценке переставляет флаг; all делает то же самое, но прекращает работу на первом нарушителе. На списке из двух тысяч оценок, где первая плохая стоит в начале, all проверит один элемент, а цикл — две тысячи.

Проверка через функцию

Когда условие сложное, его лучше вынести в отдельную функцию, а в any передать её вызов. Читается это как «есть ли элемент, который проходит проверку» — и правило живёт в одном месте, а не размазано по генератору.

правило в отдельной функции
def is_ok(mark):
    return mark >= 3 and mark <= 5

marks = [4, 2, 5, 6]

print(any(is_ok(m) for m in marks))
print(all(is_ok(m) for m in marks))
Вывод
True
False

Четвёрка проходит проверку — any отвечает True. Но шестёрка выше пяти и двойка ниже трёх, так что all отвечает False. Такое разделение удобно, когда правило сложное или его нужно переиспользовать: правило описано один раз, а any и all просто спрашивают.

любая ли строка подходит
words = ['cat', 'dog', 'bird']

print(any(len(w) > 3 for w in words))
print(all(len(w) > 3 for w in words))
Вывод
True
False

Длина больше трёх — подходит bird, поэтому any отвечает True. Но cat и dog короче, так что all отвечает False. Никакой магии: внутри генератора стоит обычное выражение, а any и all лишь спрашивают, истинно ли оно хотя бы раз или каждый раз.

  • валидация формы: all(v != '' for v in form.values()) — можно ли отправлять;
  • проверка логов: any('ошибка' in line for line in log) — случилось ли что-то;
  • страховка перед дорогой работой: if any(...) — запускать ли расчёт вообще;
  • сложное правило: вынести в функцию и звать её в генераторе;
  • счёт подходящих: sum(1 for x in xs if cond) — сколько прошло.
ВопросРучной циклФункция
Хотя бы один подходит?флаг + for + breakany(условие for x in данные)
Все подходят?флаг без переворотовall(условие for x in данные)
Пустые данныезависит от флагаany — False, all — True
  • валидация: all(поле != '' for поле in форма) — можно ли отправлять;
  • тревога: any(ошибка in строка for строка in лог) — случилось ли что-то;
  • быстрая проверка перед дорогой работой: if any(...) — запускать ли расчёт.
фрагмент: список против генератора
any([True, False])          # по готовому списку значений
any(m == 2 for m in marks)  # по генератору с условием
Фрагмент: переменная marks объявлена выше. Список хранит готовые True и False, генератор вычисляет условие для каждого элемента на лету — для больших данных это экономнее.
Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

print(any([0, 0]))
print(all([1, 2, 3]))
nums = [4, 6, 9]

print(any(n % 2 == 0 for n in nums))
words = ['', 'ok']

print(all(w for w in words))
print(any(w for w in words))
Проверь себя
0 / 8

1. Что вернёт any([0, '', 7])?

2. На какой вопрос отвечает all(m >= 3 for m in marks)?

3. Что вернёт all([])?

4. Что такое короткое замыкание any?

5. Чем any(m == 2 for m in marks) отличается от any(marks)?

6. Как одной функцией узнать, сколько элементов прошло проверку?

7. Что проверит all(all(row) for row in matrix)?

8. Что произойдёт, если заменить all(m >= 3 for m in marks) на all(marks)?

Карточки терминов
Запомнено: 0 / 9
Практика

Дан список temps = [12, 15, 3, 17]. С помощью any проверь, есть ли температура ниже 10 градусов, с помощью all — все ли температуры выше 5. Выведи оба ответа.

practice.py
Вопросы и ответы по уроку

Что делает any в Python?

Проверяет, есть ли в последовательности хотя бы один истинный элемент, и возвращает True или False. С генератором any(m == 2 for m in marks) отвечает на вопрос «есть ли хоть одна двойка» и останавливается на первом совпадении.

Урок 12: генератор списка

Чем all отличается от any?

all требует истинности всех элементов сразу и даёт False от первого нарушителя, any достаточно одного подходящего элемента. На пустой последовательности any отвечает False, all — True.

Что вернёт all для пустого списка?

True: в пустом списке нет элементов, которые ломают условие «все». Логика такая: нарушителей не найдено, проверка пройдена. Если пустой список должен означать отказ, добавьте отдельную проверку длины.

Как проверить, что все элементы списка равны числу?

Записать сравнение в генератор: all(x == 5 for x in xs). Условие вычисляется для каждого элемента, any/all сами организуют цикл и досрочную остановку. Пустой список такую проверку пройдёт — помните про это.

Как посчитать, сколько элементов списка проходят проверку?

Через sum с генератором: sum(1 for x in xs if cond). В Python True равен единице и False равен нулю, поэтому sum считает успешные проверки. Надёжнее, чем sum по сырым значениям, если в списке лежат не булевы числа.

Урок 11: счётчики и аккумуляторы

Как проверить словарь: заполнены ли все поля формы?

Обойти значения и проверить, что пустых нет: all(v != '' for v in form.values()). Если обойти сам словарь, проверятся ключи, а не содержимое полей — ключи у формы всегда непустые, и проверка пройдёт, даже когда форма пустая.

Заменяют ли any и all цикл с булевым флагом?

Для чистых вопросов «есть ли» и «все ли» — да: any(условие for ...) и all(условие for ...) делают то же самое короче и быстрее, заодно с досрочной остановкой. Флаг остаётся нужен, когда из цикла нужен сам найденный элемент, а не только ответ.

Понравился урок? Сошлитесь на него

«Функция any возвращает True, если условие выполнилось хотя бы для одного элемента, а all — если для всех без исключения.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.