Python's all(): Проверьте ваши итеративные данные на достоверность

Оглавление

При программировании вам часто приходится проверять, все ли элементы в iterable соответствуют действительности. Многократное кодирование этой функции может быть раздражающим и неэффективным. К счастью, Python предоставляет встроенную функцию all() для решения этой проблемы. Эта функция берет итерацию и проверяет все ее элементы на истинность, что удобно для определения того, обладают ли эти элементы заданным свойством или удовлетворяют определенному условию.

Программа Python all() - это мощный инструмент, который поможет вам писать чистый, читаемый и эффективный код на Python.

В этом руководстве вы узнаете, как:

  • Проверьте, все ли элементы в итерируемом элементе соответствуют действительности, используя all()
  • Используйте all() с различными повторяемыми типами
  • Объедините all() с понятиями и генераторными выражениями
  • различие между all() и логическое and оператор

Чтобы дополнить эти знания, вы закодируете несколько примеров, которые продемонстрируют интересные варианты использования all() и расскажут о множестве способов использования этой функции в программировании на Python.

Чтобы разобраться в темах этого руководства, вы должны обладать базовыми знаниями о нескольких концепциях Python, таких как повторяемые структуры данных, Логические типы, выражения, операторы, перечислите значения и генератор выражений.

Вычисление истинностного значения элементов в итерационных таблицах

Довольно распространенной проблемой в программировании является определение того, все ли элементы в списке или массиве соответствуют действительности. Например, у вас может быть следующий список условий:

  • 5 > 2
  • 1 == 1
  • 42 < 50

Чтобы выяснить, верны ли эти условия, вам нужно повторить их и проверить каждое условие на правдивость. В этом примере у вас есть, что 5 > 2 истинно, 1 == 1 истинно и 42 < 50 также истинно. В результате вы можете сказать, что все эти условия верны. Если бы хотя бы одно из условий было ложным, то вы бы сказали, что не все условия верны.

Обратите внимание, что как только вы обнаружите ложное условие, вы можете прекратить оценку условий, потому что в этом случае вы уже знаете конечный результат: не все являются истинными.

Чтобы решить эту проблему путем написания пользовательского кода на Python, вы можете использовать for цикл, чтобы выполнить итерацию по каждому условию и оценить его на предмет достоверности. Ваш цикл будет повторяться до тех пор, пока не найдет ложный элемент, после чего он остановится, потому что у вас уже есть результат:

>>> def all_true(iterable):
...     for item in iterable:
...         if not item:
...             return False
...     return True
...

Эта функция принимает повторяемый в качестве аргумента. Цикл повторяет входной аргумент, в то время как оператор условный оператор if проверяет, не является ли какой-либо элемент ложным, используя оператор not. Если элемент является ложным, то функция немедленно возвращает False, , сигнализируя о том, что не все элементы являются истинными. В противном случае он возвращается True.

Эта функция довольно универсальна. Это означает, что вы можете передать список , кортеж, строка, словарь или любую другую повторяемую структуру данных . Чтобы проверить, является ли текущий элемент истинным или ложным, all_true() использует оператор not для инвертирования истинного значения своего операнда. Другими словами, он возвращает True, если его операнд принимает значение false, и наоборот.

Логические операторы Python могут вычислять истинностное значение выражений и объектов, что гарантирует, что ваша функция может принимать повторяющиеся значения, содержащие объекты, выражения или и то, и другое. Например, если вы передаете итерацию логических выражений, то not просто вычисляет выражение и отрицает результат.

Вот all_true() в действии:

>>> bool_exps = [
...     5 > 2,
...     1 == 1,
...     42 < 50,
... ]
>>> all_true(bool_exps)
True

Поскольку все выражения во входной итерируемой переменной являются истинными, not сводит на нет результат, и блок кода if никогда не выполняется. В этом случае all_true() возвращает True.

Нечто подобное происходит, когда входная итерируемая строка содержит объекты Python и не логические выражения:

>>> objects = ["Hello!", 42, {}]
>>> all_true(objects)
False

>>> general_expressions = [
...     5 ** 2,
...     42 - 3,
...     int("42")
... ]
>>> all_true(general_expressions)
True

>>> empty = []
>>> all_true(empty)
True

В первом примере список ввода содержит обычные объекты Python, включая строку, число и словарь. В этом случае all_true() возвращает False, потому что словарь пуст и в Python он принимает значение false.

Для выполнения проверки истинности значений объектов в Python есть внутренний набор правил для объектов, которые оцениваются как ложные:

  • Изначально отрицательные константы, такие как None и False
  • Числовые типы с нулевым значением, например 0, 0.0, 0j, Decimal("0"), и Fraction(0, 1)
  • Пустые последовательности и коллекции, например "", (), [], {}, set(), и range(0)
  • Объекты, которые реализуют .__bool__() с возвращаемым значением False или .__len__() с возвращаемым значением 0

Любой другой объект оценивается как true, когда вы проверяете его на достоверность в Python.

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

В третьем примере показана важная деталь all_true(). Когда входная итерируемая строка пуста, цикл for не выполняется, и функция немедленно возвращает True. На первый взгляд такое поведение может показаться странным. Однако логика, стоящая за этим, заключается в том, что если во входных итерациях нет элементов, то нет способа определить, являются ли какие-либо элементы ложными. Итак, функция возвращает True с пустыми итерациями.

Несмотря на то, что кодирование all_true() в Python довольно простое, написание этой пользовательской функции может раздражать каждый раз, когда вам нужна ее функциональность. Определение того, все ли элементы в iterable являются истинными, является настолько распространенной задачей в программировании, что Python предоставляет встроенную функцию all() для этого задания.

Начало работы с Python all()

Если вы ознакомитесь с документацией для all() Python, то заметите, что эта функция эквивалентна функции, которую вы кодировали в предыдущем разделе. Однако, как и все встроенные функции, all() является функцией C и оптимизирована для повышения производительности.

Гвидо ван Россум предложил функции all() и any(), чтобы исключить из Python functools.reduce() и другие функциональные инструменты, такие как filter() и map(),. Однако сообщество Python было недовольно удалением этих инструментов. Несмотря на это, all() и any() были добавлены в качестве встроенных функций в Python 2.5, реализация которых была выполнена Раймондом Хеттингером.

Можно сказать, что all() в Python выполняет операцию сокращения или сворачивания, поскольку она сводит итерацию элементов к одному объекту. Однако это не функция более высокого порядка, поскольку она не принимает другие функции в качестве аргументов для выполнения своих вычислений. Итак, вы можете рассматривать all() как обычный предикат или логическую функцию.

Вы можете использовать all(), чтобы проверить, все ли элементы в iterable ввода являются истинными. Поскольку это встроенная функция, вам не нужно ее импортировать. Вот как это работает:

>>> bool_exps = [
...     5 > 2,
...     1 == 1,
...     42 < 50,
... ]
>>> all(bool_exps)
True

>>> objects = ["Hello!", 42, {}]
>>> all(objects)
False

>>> general_exps = [
...     5 ** 2,
...     42 - 3,
...     int("42")
... ]
>>> all(general_exps)
True

>>> empty = []
>>> all(empty)
True

Эти примеры показывают, что all() работает так же, как и ваша пользовательская функция, all_true(). Внутри all() перебирает элементы во входной итерации, проверяя их истинностные значения. Если он находит ложный элемент, то возвращает False. В противном случае он возвращает True.

Если вы вызываете all() с пустым iterable, как вы делали в последнем примере выше, то вы получаете True, потому что в пустом iterable нет ложного элемента. Обратите внимание, что all() вычисляет элементы во входной итерационной таблице, а не саму итерационную таблицу. Смотрите Почему all() Возвращает True, если итерационная таблица пуста? для более философской дискуссии по этому поводу.

Чтобы подвести итог поведению all(), вот таблица истинности:

Состояние Результат
Все утверждения оцениваются как верные. True
Все пункты оцениваются как ложные. False
Один или несколько пунктов оцениваются как ложные. False
Входной итератор пуст. True

Вы можете выполнить следующие вызовы all() для подтверждения информации в этой таблице:

>>> all([True, True, True])
True

>>> all([False, False, False])
False

>>> all([False, True, True])
False

>>> all([])
True

Эти примеры показывают, что all() возвращает True, когда все элементы во входной итерационной таблице равны true или когда итерационная таблица пуста. В противном случае функция возвращает False.

Так же, как и ваша функция all_true(), функция all() также реализует то, что известно как оценка короткого замыкания. Такого рода оценка означает, что all() будет возвращен, как только будет определен конечный результат операции.

Короткое замыкание происходит, когда функция находит ложный элемент в итерационной таблице. В этом случае нет необходимости оценивать остальные элементы, поскольку функция уже знает конечный результат. Обратите внимание, что этот тип реализации означает, что вы можете получить различное поведение при тестировании условий с побочными эффектами. Рассмотрим следующий пример:

>>> def is_true(value):
...     print("Side effect!")
...     return bool(value)
...

>>> values = [0, 1]

>>> conditions = (is_true(n) for n in values)
>>> all(conditions)
Side effect!
False

>>> conditions = (is_true(n) for n in reversed(values))
>>> all(conditions)
Side effect!
Side effect!
False

Функция is_true() принимает объект в качестве аргумента и возвращает его истинное значение. Во время выполнения функции возникает побочный эффект: функция выводит что-то на экран.

Первый экземпляр conditions содержит генераторное выражение, которое выдает значения истинности после ленивого вычисления каждого элемента из входной итерабельный параметр, который в примере равен values. На этот раз all() вычисляет функцию только один раз, потому что is_true(0) возвращает False. Побочный эффект запускается только один раз.

Теперь ознакомьтесь со вторым экземпляром conditions. Если вы измените входную итерацию, то all() вычисляет оба элемента, потому что вызов is_true() с 1 в качестве аргумента возвращает True. Побочный эффект проявляется дважды.

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

Наконец, когда дело доходит до использования функции all(), вы можете сказать, что у нее есть как минимум два общих варианта использования. Вы можете использовать all(), чтобы проверить, все ли элементы являются повторяемыми:

  1. Значение равно true
  2. Обладают заданным свойством или удовлетворяют определенному условию

В следующем разделе вы узнаете, как использовать all() с различными итерируемыми типами в Python. После этого вы узнаете, как использовать all() с понятиями списка и генераторными выражениями для решения второго варианта использования, указанного выше.

Использование all() С различными повторяющимися типами

Встроенная функция all() поддерживает стиль ввода текста в Python и принимает различные типы аргументов, если они доступны для повторения. Вы можете использовать all() со списками, кортежами, строками, словарями, наборами и тому подобным.

Во всех случаях all() выполняет свою работу должным образом, возвращая True, если все элементы соответствуют действительности, и False в противном случае. В этом разделе вы приведете примеры кода, используя all() с различными повторяемыми типами.

Последовательности

На данный момент вы узнали, как all() работает со списками Python. В этом разделе вы узнаете, что на самом деле нет никакой разницы между списками и другими типами данных последовательности, такими как кортежи и range объекты. Все, что нужно функции, - это чтобы входной объект был повторяемым.

Вот несколько примеров использования all() с кортежами и range объектами:

>>> # With tuples
>>> all((1, 2, 3))
True
>>> all((0, 1, 2, 3))
False
>>> all(())
True
>>> all(tuple())
True

>>> # With range objects
>>> all(range(10))
False
>>> all(range(1, 11))
True
>>> all(range(0))
True

Как обычно, если все элементы во входной итерационной таблице соответствуют действительности, то вы получите True. В противном случае вы получите False. Пустые кортежи и диапазоны приводят к результату True. В последнем примере вызов range() с 0 в качестве аргумента возвращает пустой объект range, поэтому all() в результате выдает True.

Вы также можете передавать кортежи, содержащие выражения, логические выражения или объекты Python любого типа, в all(). Попробуйте!

Словари

Словари - это коллекции пар ключ-значение. Если вы выполняете итерацию по словарю напрямую, то автоматически выполняете цикл по его ключам. Кроме того, вы можете использовать удобные методы для явной итерации ключей, значений и элементов словарей.

Примечание: Использование all() с помощью .items() метода словаря не имеет особого смысла. Этот метод возвращает пары ключ-значение в виде кортежей из двух элементов, которые в Python всегда будут иметь значение true.

Если вы передадите словарь непосредственно в all(), то функция автоматически проверит ключи словаря:

>>> all({"gold": 1, "silver": 2, "bronze": 3})
True

>>> all({0: "zero", 1: "one", 2: "two"})
False

Поскольку все ключи в первом словаре верны, в результате вы получите True. Во втором словаре первым ключом является 0, значение которого равно false. В этом случае вы получите False обратно из all().

Если вы хотите получить тот же результат, что и в примерах выше, но с более читаемым и понятным кодом, то вы можете использовать метод .keys(), который возвращает все ключи из базового словаря:

>>> medals = {"gold": 1, "silver": 2, "bronze": 3}
>>> all(medals.keys())
True

>>> numbers = {0: "zero", 1: "one", 2: "two"}
>>> all(numbers.keys())
False

С помощью .keys() вы явно указываете, что ваш код вызывает all(), чтобы определить, являются ли все текущие ключи во входном словаре истинными.

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

>>> monday_inventory = {"book": 2, "pencil": 5, "eraser": 1}
>>> all(monday_inventory.values())
True

>>> tuesday_inventory = {"book": 2, "pencil": 3, "eraser": 0}
>>> all(tuesday_inventory.values())
False

В этих примерах вы сначала проверяете, есть ли в вашем текущем ассортименте школьных принадлежностей хотя бы один предмет. В понедельник во всех ваших товарах будет по крайней мере одна единица, поэтому all() возвращается True. Однако во вторник вызов на all() возвращает False, потому что у вас закончились единицы по крайней мере в одном из ваших запасов, в данном случае eraser.

Использование all() С понятиями и генераторными выражениями

Как вы узнали ранее, второй вариант использования all() в Python заключается в проверке того, все ли элементы в iterable обладают заданным свойством или удовлетворяют определенному условию. Чтобы выполнить такого рода проверку, вы можете использовать all() с понятием списка или выражением генератора в качестве аргумента, в зависимости от ваших потребностей.

Синергия, которую вы получаете, комбинируя all() со списковыми понятиями и генераторными выражениями, раскрывает всю мощь этой функции и делает ее весьма полезной при повседневном написании кода.

Одним из способов использовать эту сверхспособность all() является использование функции-предиката, которая проверяет наличие желаемого свойства. Эта функция-предикат будет выражением в представлении списка, которое вы передадите в качестве аргумента all(). Вот необходимый синтаксис:

all([predicate(item) for item in iterable])

При понимании этого списка используется predicate() для проверки каждого item из iterable для данного свойства. Затем вызов all() уменьшает результирующий список до одного значения True или False, которое сообщает вам, все ли элементы обладают свойством, которое predicate() определяет и проверяет.

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

>>> import math

>>> def is_prime(n):
...     if n <= 1:
...         return False
...     for i in range(2, math.isqrt(n) + 1):
...         if n % i == 0:
...             return False
...     return True
...

>>> numbers = [2, 3, 5, 7, 11]
>>> all([is_prime(x) for x in numbers])
True

>>> numbers = [2, 4, 6, 8, 10]
>>> all([is_prime(x) for x in numbers])
False

В этом примере вы комбинируете all() с понятием списка. При понимании используется функция предиката is_prime() для проверки каждого значения в numbers на предмет простоты. Результирующий список будет содержать логические значения (True или False) для результата каждой проверки. Затем all() получает этот список в качестве аргумента и обрабатывает его, чтобы определить, все ли числа являются простыми или нет.

Примечание: Предикат is_prime() основан на алгоритме из статьи Википедии о тестах на первичность.

Второй вариант использования этой волшебной комбинации, all() плюс понимание списка, заключается в проверке того, все ли элементы в iterable соответствуют заданному условию. Вот необходимый синтаксис:

all([condition for item in iterable])

Этот вызов all() использует анализ списка, чтобы проверить, все ли элементы в iterable удовлетворяют требуемому condition, который обычно определяется в терминах отдельного item. Следуя этой идее, вот несколько примеров, которые проверяют, все ли числа в списке больше, чем 0:

>>> numbers = [1, 2, 3]
>>> all([number > 0 for number in numbers])
True

>>> numbers = [-2, -1, 0, 1, 2]
>>> all([number > 0 for number in numbers])
False

В первом примере all() возвращает True, поскольку все числа во входном списке удовлетворяют условию быть больше 0. Во втором примере результатом будет False, поскольку входная итерируемая величина включает 0 и отрицательные числа.

Как вы уже знаете, all() возвращает True с пустой итерацией в качестве аргумента. Такое поведение может показаться странным и привести к неправильным выводам:

>>> numbers = []

>>> all([number < 0 for number in numbers])
True

>>> all([number == 0 for number in numbers])
True

>>> all([number > 0 for number in numbers])
True

Этот код показывает, что все значения в numbers меньше, чем 0, но они также равны и больше, чем 0, что невозможно. Основная причина этого нелогичного результата заключается в том, что все эти вызовы all() вычисляют пустые итеративные значения, что приводит к возврату all() True.

Чтобы обойти эту проблему, вы можете использовать встроенную функцию len(), чтобы получить количество элементов во входной итерационной таблице. Если len() возвращает 0, то вы можете пропустить вызов all(), чтобы обработать пустую входную итерацию. Эта стратегия сделает ваш код менее подверженным ошибкам.

Во всех примерах, которые вы написали в этом разделе, в качестве аргумента для all() используется понимание списка. Понимание списка создает полный список в памяти, что может быть расточительной операцией. Такое поведение особенно верно, если вам больше не нужен результирующий список в вашем коде, что является типичным случаем с all().

В этой ситуации всегда эффективнее использовать all() вместо генерирующего выражения, особенно если вы работаете с длинным списком входных данных. Вместо создания всего нового списка в памяти, генераторное выражение будет генерировать элементы и выдавать их по запросу, что сделает ваш код более эффективным.

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

# With a predicate
all(predicate(item) for item in iterable)

# With a condition
all(condition for item in iterable)

Единственное отличие заключается в том, что в генераторном выражении используются круглые скобки (()) вместо квадратных скобок ([]). Поскольку для вызова функции уже требуются круглые скобки, вам просто нужно убрать квадратные скобки.

В отличие от списковых выражений, выражения-генераторы выдают элементы по запросу, что делает их весьма эффективными с точки зрения использования памяти. Кроме того, вы не создадите новый список, чтобы просто выбросить его после возврата all().

Сравнение all() С and Логическим оператором

Вы можете приблизительно представить себе all() как последовательность элементов, соединенных логическим оператором and. Например, вызов функции all([item1, item2, ..., itemN]) семантически эквивалентен выражению item1 and item2 ... and itemN. Однако между ними есть некоторые незначительные различия.

В этом разделе вы узнаете об этих различиях. Первое из них связано с синтаксисом, а второе - с возвращаемым значением. Кроме того, вы узнаете, что как оператор all(), так и оператор and реализуют оценку короткого замыкания.

Понимание различий в синтаксисе

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

>>> all([True, False])
False

Элементы в iterable ввода могут быть общими выражениями, логическими выражениями или объектами Python любого типа. Кроме того, количество элементов в iterable ввода зависит только от объема доступной памяти в вашей системе.

С другой стороны, оператор and является бинарным оператором , который соединяет два операнда в выражении:

>>> True and False
False

Логический оператор and использует левый и правый операнды для построения составного выражения. Как и в случае с all(), операндами в выражении and могут быть общие выражения, логические выражения или объекты Python. Наконец, вы можете использовать несколько операторов and для соединения любого количества операндов.

Возвращает логические значения в зависимости от операндов

Второе и даже более важное различие между all() и оператором and заключается в их соответствующих возвращаемых значениях. В то время как all() всегда возвращает True или False, оператор and всегда возвращает один из своих операндов. Он возвращает только True или False, если возвращаемый операнд явно принимает любое из значений:

>>> all(["Hello!", 42, {}])
False
>>> "Hello!" and 42 and {}
{}

>>> all([1, 2, 3])
True
>>> 1 and 2 and 3
3

>>> all([0, 1, 2, 3])
False
>>> 0 and 1 and 2 and 3
0

>>> all([5 > 2, 1 == 1])
True
>>> 5 > 2 and 1 == 1
True

Эти примеры показывают, что all() всегда возвращает True или False, что согласуется со статусом функции-предиката. С другой стороны, and возвращает последний вычисленный операнд. Если это последний операнд в выражении, то все предыдущие, должно быть, были истинными. В противном случае and вернет первый ошибочный операнд, указывающий, на каком этапе вычисления был остановлен.

Обратите внимание, что в последнем примере оператор and возвращает True, поскольку подразумеваемыми операндами являются выражения сравнения, которые всегда возвращают True или False явно.

Это важное различие между функцией all() и оператором and. Поэтому вам следует учитывать это, чтобы предотвратить появление незначительных ошибок в вашем коде. Однако в логических контекстах, таких как if операторы и while циклы, это различие не имеет значения при все.

Короткое замыкание при оценке

Как вы уже узнали, all() завершает оценку элементов во входной итерации, когда она определяет конечный результат. Оператор and также реализует оценку короткого замыкания.

Преимущество этой функции заключается в том, что она делает операцию эффективной, пропуская оставшиеся проверки, как только появляется ложный элемент.

Чтобы опробовать оценку короткого замыкания, вы можете использовать функцию генератора , как в следующем примере:

>>> def generate_items(iterable):
...     for i, item in enumerate(iterable):
...         print(f"Checking item: {i}")
...         yield item
...

Цикл внутри generate_items() выполняет итерацию по элементам в iterable, используя встроенную функцию enumerate() для получения индекса каждого проверяемого элемента. Затем цикл выводит сообщение, идентифицирующее проверенный элемент, и выдает имеющийся в наличии элемент.

Установив generate_items(), вы можете запустить следующий код для проверки all() на наличие короткого замыкания:

>>> # Check both items to get the result
>>> items = generate_items([True, True])
>>> all(items)
Checking item: 0
Checking item: 1
True

>>> # Check the first item to get the result
>>> items = generate_items([False, True])
>>> all(items)
Checking item: 0
False

>>> # Still have a remaining item
>>> next(items)
Checking item: 1
True

Первый вызов all() показывает, как функция проверяет оба элемента для определения конечного результата. Второй вызов подтверждает, что all() проверяет только первый элемент. Поскольку этот элемент является ложным, функция возвращает результат немедленно, не проверяя второй элемент. Вот почему генератор по-прежнему выдает второй элемент при вызове next().

Теперь вы можете запустить аналогичный тест, используя оператор and:

>>> # Check both items to get the result
>>> items = generate_items([True, True])
>>> next(items) and next(items)
Checking item: 0
Checking item: 1
True

>>> # Check the first item to get the result
>>> items = generate_items([False, True])
>>> next(items) and next(items)
Checking item: 0
False

>>> # Still have a remaining item
>>> next(items)
Checking item: 1
True

Первое выражение and вычисляет оба операнда для получения конечного результата. Второе выражение and вычисляет только первый операнд для определения результата. Вызов next() с items в качестве аргумента показывает, что функция-генератор по-прежнему выдает оставшийся элемент.

Приведение all() В действие: Практические примеры

На данный момент вы изучили основы языка Python all(). Вы научились использовать его с последовательностями, словарями, понятиями списков и генераторами выражений. Кроме того, вы узнали о различиях и сходствах между этой встроенной функцией и логическим оператором and.

В этом разделе вы приведете ряд практических примеров, которые помогут вам оценить, насколько полезным может быть all() при программировании на Python. Так что следите за обновлениями и наслаждайтесь программированием!

Улучшение читаемости длинных составных условий

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

Например, предположим, что вам нужно проверить правильность ввода пользователем фрагмента кода. Чтобы ввод был корректным, это должно быть целое число от 0 до 100, которое также является четным числом. Чтобы проверить все эти условия, вы можете использовать следующую инструкцию if:

>>> x = 42

>>> if isinstance(x, int) and 0 <= x <= 100 and x % 2 == 0:
...     print("Valid input")
... else:
...     print("Invalid input")
...
Valid input

Условие if состоит из вызова функции isinstance(), которая проверяет, является ли входное значение целым числом, цепное сравнение выражение, которое проверяет, находится ли число между 0 и 100, и выражение, которое проверяет, является ли входное значение четным числом.

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

Чтобы улучшить читаемость этого условия, вы можете использовать all(), как в следующем коде:

>>> x = 42

>>> validation_conditions = (
...     isinstance(x, int),
...     0 <= x <= 100,
...     x % 2 == 0,
... )

>>> if all(validation_conditions):
...     print("Valid input")
... else:
...     print("Invalid input")
...
Valid input

В этом примере все условия проверки хранятся в кортеже с описательным именем. Использование этого метода имеет дополнительное преимущество: если вам когда-нибудь понадобится добавить новое условие проверки, вам просто нужно добавить новую строку в свой кортеж validation_conditions. Обратите внимание, что теперь ваш оператор if содержит довольно читаемое, явное и краткое выражение, основанное на all().

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

>>> def is_integer(x):
...     return isinstance(x, int)
...

>>> def is_between(a=0, b=100):
...     return lambda x: a <= x <= b
...

>>> def is_even(x):
...     return x % 2 == 0
...

>>> validation_conditions = (
...     is_integer,
...     is_between(0, 100),
...     is_even,
... )

>>> for x in (4.2, -42, 142, 43, 42):
...     print(f"Is {x} valid?", end=" ")
...     print(all(condition(x) for condition in validation_conditions))
...
Is 4.2 valid? False
Is -42 valid? False
Is 142 valid? False
Is 43 valid? False
Is 42 valid? True

В этом примере у вас есть три функции, которые повторно проверяют три исходных условия. Затем вы переопределяете свой набор условий проверки, используя функции, которые вы только что закодировали. В заключительном цикле for показано, как вы можете повторно использовать эти функции для проверки нескольких входных объектов с помощью all().

Проверка повторяемости числовых значений

Еще один интересный вариант использования all() заключается в проверке того, находятся ли все числовые значения в iterable в заданном интервале. Вот несколько примеров того, как это сделать для различных условий и с помощью генераторного выражения:

>>> numbers = [10, 5, 6, 4, 7, 8, 20]

>>> # From 0 to 20 (Both included)
>>> all(0 <= x <= 20 for x in numbers)
True

>>> # From 0 to 20 (Both excluded)
>>> all(0 < x < 20 for x in numbers)
False

>>> # From 0 to 20 (integers only)
>>> all(x in range(21) for x in numbers)
True

>>> # All greater than 0
>>> all(x > 0 for x in numbers)
True

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

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

Проверка строк и повторяющихся значений строк

Встроенный str type реализует несколько методов string с предикатами, которые могут быть полезны, когда вам нужно проверить повторяемость строк и отдельных символов в данной строке.

Например, с помощью этих методов вы можете проверить, является ли строка допустимым десятичным числом, буквенно-цифровым символом или допустимым символом ASCII.

Вот несколько примеров использования строковых методов в вашем коде:

>>> numbers = ["1", "2", "3.0"]

>>> all(number.isdecimal() for number in numbers)
True

>>> chars = "abcxyz123"

>>> all(char.isalnum() for char in chars)
True

>>> all(char.isalpha() for char in chars)
False

>>> all(char.isascii() for char in chars)
True

>>> all(char.islower() for char in chars)
False

>>> all(char.isnumeric() for char in chars)
False

>>> all(char.isprintable() for char in chars)
True

Каждый из этих .is*() методов проверяет определенное свойство базовой строки. Вы можете воспользоваться этими и несколькими другими строковыми методами для проверки элементов в итерационном наборе строк, а также отдельных символов в данной строке.

Удаление Строк С Пустыми Полями Из Табличных Данных

При работе с табличными данными вы можете столкнуться с проблемой наличия пустых полей. Возможно, вам потребуется очистить строки, содержащие пустые поля. Если это так, то вы можете использовать all() и filter() для извлечения строк, во всех полях которых есть данные.

Встроенная функция filter() принимает в качестве аргументов функциональный объект и итерируемую функцию. Как правило, в качестве первого аргумента filter() используется функция-предикат. Вызов filter() применяет предикат к каждому элементу в iterable и возвращает итератор с элементами, которые возвращает предикат True.

Вы можете использовать all() в качестве предиката в вызове filter(). Таким образом, вы можете обрабатывать списки списков, что может быть полезно при работе с табличными данными.

Для конкретного примера, допустим, у вас есть CSV-файл с данными о сотрудниках вашей компании:

name,job,email
"Linda","Technical Lead",""
"Joe","Senior Web Developer","joe@example.com"
"Lara","Project Manager","lara@example.com"
"David","","david@example.com"
"Jane","Senior Python Developer","jane@example.com"

При беглом просмотре этого файла вы заметите, что некоторые строки содержат пустые поля. Например, в первой строке нет адреса электронной почты, а в четвертой строке не указана должность или роль. Вам необходимо очистить данные, удалив строки, содержащие пустые поля.

Вот как вы можете удовлетворить это требование, используя all() в качестве предиката в вызове filter():

>>> import csv
>>> from pprint import pprint

>>> with open("employees.csv", "r") as csv_file:
...     raw_data = list(csv.reader(csv_file))
...

>>> # Before cleaning
>>> pprint(raw_data)
[['name', 'job', 'email'],
 ['Linda', 'Technical Lead', ''],
 ['Joe', 'Senior Web Developer', 'joe@example.com'],
 ['Lara', 'Project Manager', 'lara@example.com'],
 ['David', '', 'david@example.com'],
 ['Jane', 'Senior Python Developer', 'jane@example.com']]

>>> clean_data = list(filter(all, raw_data))

>>> # After cleaning
>>> pprint(clean_data)
[['name', 'job', 'email'],
 ['Joe', 'Senior Web Developer', 'joe@example.com'],
 ['Lara', 'Project Manager', 'lara@example.com'],
 ['Jane', 'Senior Python Developer', 'jane@example.com']]

В этом примере вы сначала загружаете содержимое целевого CSV-файла в raw_data с помощью модуля csv из стандартной библиотеки Python . Вызов функции pprint() показывает, что данные содержат строки с пустыми полями. Затем вы очищаете данные с помощью filter() и all().

Примечание: Если вам неудобно использовать filter(), то вы можете заменить его на понимание списка.

Продолжайте и запустите вместо этого следующую строку кода:

>>> clean_data = [row for row in raw_data if all(row)]

Как только у вас будет список чистых данных, вы можете снова запустить цикл for, чтобы проверить, все ли работает нормально.

Как функции filter() и all() взаимодействуют для выполнения задачи? Что ж, если all() находит пустое поле в строке, то возвращает False. При таком результате filter() не будет включать эту строку в окончательные данные. Чтобы убедиться, что этот метод работает, вы можете вызвать pprint(), указав чистые данные в качестве аргумента.

Сравнение пользовательских структур данных

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

Чтобы создать этот пользовательский класс, вы можете создать подкласс UserList из модуля collections , а затем переопределить вызываемый специальный метод .__gt__(). Переопределение этого метода позволяет вам перегрузить оператор greater than (>), предоставив для него пользовательское поведение:

>>> from collections import UserList

>>> class ComparableList(UserList):
...     def __gt__(self, threshold):
...         return all(x > threshold for x in self)
...

>>> numbers = ComparableList([1, 2, 3])

>>> numbers > 0
True

>>> numbers > 5
False

В .__gt__() вы используете all(), чтобы проверить, все ли числа в текущем списке больше определенного значения threshold, которое должно быть получено от пользователя.

Выражения сравнения в конце этого фрагмента кода показывают, как использовать ваш пользовательский список и как он ведет себя с оператором greater than (>). В первом выражении все значения в списке больше, чем 0, поэтому результатом будет True. Во втором выражении все числа меньше 5, что приводит к результату False.

Частично эмулирует функцию Python zip()

Встроенная функция Python zip() полезна для параллельного перебора нескольких итераций. Эта функция принимает заданное количество итераций (N) в качестве аргументов и объединяет элементы из каждой из них в N-элементов кортежей. В этом примере вы узнаете, как использовать all() для частичного моделирования этой функциональности.

Чтобы лучше понять задачу, ознакомьтесь с основами того, что делает zip():

>>> numbers = zip(["one", "two"], [1, 2])

>>> list(numbers)
[('one', 1), ('two', 2)]

В этом примере вы передаете два списка в качестве аргументов zip(). Функция возвращает итератор, который выдает кортежи по два элемента в каждом, что вы можете подтвердить, вызвав list() с результирующим итератором в качестве аргумента.

Вот функция, которая имитирует эту функциональность:

>>> def emulated_zip(*iterables):
...     lists = [list(iterable) for iterable in iterables]
...     while all(lists):
...         yield tuple(current_list.pop(0) for current_list in lists)
...

>>> numbers = emulated_zip(["one", "two"], [1, 2])

>>> list(numbers)
[('one', 1), ('two', 2)]

Ваша функция emulated_zip() может принимать переменное количество аргументов, состоящих из повторяемых объектов. Первая строка внутри функции использует понимание списка, чтобы преобразовать каждую входную итерацию в список Python, чтобы позже вы могли использовать ее метод .pop(). Условие цикла зависит от all(), чтобы проверить, содержат ли все входные списки хотя бы один элемент.

На каждой итерации оператор yield возвращает кортеж, содержащий по одному элементу из каждого входного списка. Вызов .pop() с 0 в качестве аргумента извлекает и удаляет первый элемент из каждого списка.

Как только цикл повторяется достаточное количество раз, чтобы .pop() удалить все элементы из любого списка, условие становится ложным, и функция завершается. Цикл завершается, когда исчерпывается самая короткая итерация, при этом более длинные итерации сокращаются. Такое поведение согласуется с поведением по умолчанию zip().

Обратите внимание, что ваша функция лишь частично эмулирует встроенную функцию zip(), поскольку ваша функция не принимает аргумент strict. Этот аргумент был добавлен в Python 3.10 как безопасный способ обработки итераций разной длины.

Заключение

Теперь вы знаете, как проверить, все ли элементы в существующем iterable соответствуют действительности, используя встроенную в Python функцию all(). Вы также знаете, как использовать эту функцию, чтобы узнать, соответствуют ли элементы в итерационной таблице заданному условию или обладают определенным свойством.

Обладая этими знаниями, вы теперь сможете писать более читаемый и эффективный код на Python.

В этом уроке вы узнали:

  • Как использовать Python all() для проверки того, все ли элементы в iterable соответствуют действительности
  • Как all() работает с различными итерируемыми типами
  • Как объединить all() с понятиями и генераторными выражениями
  • Чем all()отличается от оператора and и чем он похож на него

Кроме того, вы написали несколько практических примеров, которые помогли вам понять, насколько мощным может быть all() и каковы некоторые из наиболее распространенных вариантов его использования в программировании на Python.

Back to Top