Вопрос-Ответ

How do "and" and "or" act with non-boolean values?

Как "и" и "или" действуют с небулевыми значениями?

Я пытаюсь изучить python и наткнулся на некоторый код, который хорош и короток, но не совсем имеет смысл

контекст был:

def fn(*args):
return len(args) and max(args)-min(args)

Я понимаю, что он делает, но почему python делает это - т. е. Возвращает значение, а не True / False?

10 and 7-2

возвращает 5. Аналогично, изменение "и" на "или" приведет к изменению функциональности. Итак

10 or 7 - 2

Вернет 10.

Это законный / надежный стиль, или есть какие-то подводные камни по этому поводу?

Переведено автоматически
Ответ 1

TL; DR

Мы начнем с обобщения поведения двух логических операторов and и or. Эти идиомы лягут в основу нашего обсуждения ниже.


and


Возвращает первое ложное значение, если таковое имеется, в противном случае возвращает последнее значение в выражении.



or


Возвращает первое истинное значение, если таковое имеется, в противном случае возвращает последнее значение в выражении.


Поведение также кратко описано в документации, особенно в этой таблице:























ОперацияРезультат
x or yесли x равно false, то y, иначе x
x and yесли x равно false, то x, иначе y
not xесли x равно false, то True, else False

Единственный оператор, возвращающий логическое значение независимо от его операндов, - это not operator .


Оценки "правдивости" и "Правдивости"

Инструкция

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

Should (roughly) translate to:

r1 = exp1
if not r1:
r1 = exp2

Or, equivalently,

r1 = exp1 if exp1 else exp2

Where exp1 and exp2 are arbitrary python objects, or expressions that return some object. The key to understanding the uses of the logical and and or operators here is understanding that they are not restricted to operating on, or returning boolean values. Any object with a truthiness value can be tested here. This includes int, str, list, dict, tuple, set, NoneType, and user defined objects. Short circuiting rules still apply as well.

But what is truthiness?

It refers to how objects are evaluated when used in conditional expressions. @Patrick Haugh summarises truthiness nicely in this post.


All values are considered "truthy" except for the following, which are
"falsy":



  • None

  • False

  • 0

  • 0.0

  • 0j

  • Decimal(0)

  • Fraction(0, 1)

  • [] - an empty list

  • {} - an empty dict

  • () - an empty tuple

  • '' - an empty str

  • b'' - an empty bytes

  • set() - an empty set

  • an empty range, like range(0)

  • objects for which

    • obj.__bool__() returns False

    • obj.__len__() returns 0




A "truthy" value will satisfy the check performed by if or while
statements. We use "truthy" and "falsy" to differentiate from the
bool values True and False.



How and Works

We build on OP's question as a segue into a discussion on how these operators in these instances.


Given a function with the definition


def foo(*args):
...

How do I return the difference between the minimum and maximum value
in a list of zero or more arguments?


Найти минимум и максимум несложно (используйте встроенные функции!). Единственная загвоздка здесь заключается в правильной обработке углового случая, когда список аргументов может быть пустым (например, вызов foo()). Мы можем выполнить и то, и другое в одной строке благодаря оператору and:

def foo(*args):
return len(args) and max(args) - min(args)
foo(1, 2, 3, 4, 5)
# 4

foo()
# 0

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

В приведенной выше функции, если foo получает один или несколько аргументов, то len(args) больше 0 (положительное число), поэтому возвращаемый результат равен max(args) - min(args). OTOH, если аргументы не переданы, len(args) is 0, который является ложным, и 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)
# [9004]

foo(1, 2, 3, 4)
# 'No number over 9000!'

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

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

def foo(*args):
r = [x for x in args if x > 9000]
if not r:
return 'No number over 9000!'

return r

Как и прежде, эта структура более гибкая с точки зрения обработки ошибок.

Ответ 2

Цитирую из документов по Python


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


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

Чтобы получить логическое значение, просто введите его.

return bool(len(args) and max(args)-min(args))

Почему?

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

Например:

2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too
0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all

То же самое относится и к or too , то есть оно вернет правдивое выражение, как только найдет его, потому что вычисление остальной части выражения избыточно.

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


Чтобы узнать, что является правдивым, а что ложным, ознакомьтесь с ответом Патрика Хо

Ответ 3

и и или выполняют логическую логику, но при сравнении возвращают одно из фактических значений. При использовании и значения вычисляются в логическом контексте слева направо. 0, ", [], (), {}, и None являются false в логическом контексте; все остальное равно true.

Если все значения истинны в логическом контексте, и возвращает последнее значение.

>>> 2 and 5
5
>>> 2 and 5 and 10
10

Если какое-либо значение равно false в логическом контексте и возвращает первое значение false.

>>> '' and 5
''
>>> 2 and 0 and 5
0

Итак, код

return len(args) and max(args)-min(args)

возвращает значение max(args)-min(args) при наличии аргументов, иначе оно возвращает len(args) значение 0.

Ответ 4

Это законный / надежный стиль, или есть какие-то подводные камни по этому поводу?


Это законно, это вычисление короткого замыкания, при котором возвращается последнее значение.

Вы приводите хороший пример. Функция вернет 0, если аргументы не переданы, и коду не нужно проверять наличие особого случая отсутствия переданных аргументов.

Другой способ использовать это - использовать аргументы None по умолчанию для изменяемого примитива, например, для пустого списка:

def fn(alist=None):
alist = alist or []
....

If some non-truthy value is passed to alist it defaults to an empty list, handy way to avoid an if statement and the mutable default argument pitfall

python