TL; DR
Мы начнем с резюмирования поведения двух логических операторов andи or. Эти идиомы лягут в основу нашего обсуждения ниже.
and
Вернуть первое значение Falsy, если оно есть, иначе вернуть последнее значение в выражении.
or
Верните первое значение Truthy, если оно есть, иначе верните последнее значение в выражении.
Поведение также кратко описано в документации , особенно в этой таблице:

Единственный оператор, возвращающий логическое значение независимо от его операндов, - это notоператор.
«Правдивость» и «Истина» оценки
Заявление
len(args) and max(args) - min(args)
Это очень питонический лаконичный (и, возможно, менее читаемый) способ сказать «если argsне пусто, вернуть результат max(args) - min(args)», в противном случае вернуть 0. В общем, это более сжатое представление if-elseвыражения. Например,
exp1 and exp2
Должен (примерно) переводиться как:
r1 = exp1
if r1:
r1 = exp2
Или, что то же самое,
r1 = exp2 if exp1 else exp1
По аналогии,
exp1 or exp2
Должен (примерно) переводиться как:
r1 = exp1
if not r1:
r1 = exp2
Или, что то же самое,
r1 = exp1 if exp1 else exp2
Где exp1и exp2- произвольные объекты Python или выражения, возвращающие некоторый объект. Ключ к пониманию использования логического andи orоператоров здесь является понимание того, что они не ограничиваются , работающих на, или возвращать логические значения. Здесь можно проверить любой объект со значением истинности. Это включает в себя int, str, list, dict, tuple, set, NoneType, и определенные пользователем объекты. Правила короткого замыкания также применяются.
Но что такое правдивость?
Это относится к тому, как объекты оцениваются при использовании в условных выражениях. @Patrick Haugh красиво резюмирует правдивость в этом посте .
Все значения считаются «правдивыми», за исключением следующих, которые являются «ложными»:
None
False
0
0.0
0j
Decimal(0)
Fraction(0, 1)
[] - пустой list
{} - пустой dict
() - пустой tuple
'' - пустой str
b'' - пустой bytes
set() - пустой set
- пустой
range, какrange(0)
- объекты, для которых
obj.__bool__() возвращается False
obj.__len__() возвращается 0
«Правдивое» значение удовлетворяет проверке, выполняемой операторами ifили while
. Мы используем «правду» и «ложь», чтобы отличать от
boolзначений Trueи False.
Как andработает
Мы опираемся на вопрос OP как продолжение обсуждения того, как работают эти операторы в этих случаях.
Для функции с определением
def foo(*args):
...
Как мне вернуть разницу между минимальным и максимальным значением в списке из нуля или более аргументов?
Найти минимум и максимум очень просто (используйте встроенные функции!). Единственная загвоздка здесь - это правильная обработка углового случая, когда список аргументов может быть пустым (например, вызов foo()). Мы можем сделать и то, и другое в одной строке благодаря andоператору:
def foo(*args):
return len(args) and max(args) - min(args)
foo(1, 2, 3, 4, 5)
foo()
Поскольку andиспользуется, второе выражение также должно быть вычислено, если первое True. Обратите внимание: если первое выражение оценивается как истинное, возвращаемое значение всегда является результатом второго выражения . Если первое выражение оценивается как ложное, то возвращаемый результат является результатом первого выражения.
В приведенной выше функции If fooполучает один или несколько аргументов, len(args)больше чем 0(положительное число), поэтому возвращаемый результат равен max(args) - min(args). Ото, если не передаются аргументы, len(args)в 0которых есть Falsy, и 0возвращается.
Обратите внимание, что альтернативный способ написать эту функцию:
def foo(*args):
if not len(args):
return 0
return max(args) - min(args)
Или, точнее,
def foo(*args):
return 0 if not args else max(args) - min(args)
Конечно, ни одна из этих функций не выполняет проверку типов, поэтому, если вы полностью не доверяете предоставленным входным данным, не полагайтесь на простоту этих конструкций.
Как orработает
Я объясняю работу orподобным образом на надуманном примере.
Для функции с определением
def foo(*args):
...
Как бы вы закончили, fooчтобы вернуть все числа 9000?
Здесь мы используем orугловой корпус. Мы определяем fooкак:
def foo(*args):
return [x for x in args if x > 9000] or 'No number over 9000!'
foo(9004, 1, 2, 500)
foo(1, 2, 3, 4)
fooвыполняет фильтрацию списка, чтобы сохранить все числа 9000. Если такие числа существуют, результатом понимания списка является непустой список, который является Истинным, поэтому он возвращается (здесь происходит короткое замыкание). Если таких чисел не существует, то результатом составления списка будет []Ложь. Итак, второе выражение теперь вычисляется (непустая строка) и возвращается.
Используя условные выражения, мы могли бы переписать эту функцию как,
def foo(*args):
r = [x for x in args if x > 9000]
if not r:
return 'No number over 9000!'
return r
Как и прежде, эта структура более гибкая с точки зрения обработки ошибок.
and(а такжеor) не ограничивается работой или возвратом логических значений.