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

Я пытаюсь изучить 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.

Является ли этот законный/надежный стиль, или есть какие-то ошибки?

+82
30 окт. '17 в 3:16
источник поделиться
8 ответов

TL; DR

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

and

Вернуть первое значение Falsy, если оно есть, иначе вернуть последнее значение в выражении.

or

Верните первое значение Truthy, если оно есть, иначе верните последнее значение в выражении.

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

enter image description here

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


Оценки "Истина" и "Истина"

Заявление

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

Это очень питонический лаконичный (и, возможно, менее читабельный) способ сказать "если args не пуст, вернуть результат max(args) - min(args) ", в противном случае вернуть 0. В общем, это более краткое представление выражения if-else. Например,

exp1 and 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)
# 4

foo()
# 0

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

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

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

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

    return r

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

+116
30 окт. '17 в 3:28
источник

Цитата из Документы 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 no need to check 3 at all

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

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


Чтобы узнать, что Truthy и Falsey, проверьте ответ Патрика Хога

+14
30 окт. '17 в 3:23
источник
другие ответы

Связанные вопросы


Похожие вопросы

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

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

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

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

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

Итак, код

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

возвращает значение max(args)-min(args), когда есть args else, он возвращает len(args), который равен 0.

+7
30 окт. '17 в 3:33
источник

Является ли этот законный/надежный стиль, или есть какие-то ошибки?

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

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

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

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

Если какое-либо значение, не относящееся к правде, передается, чтобы alist умолчанию имеет пустой список, удобный способ избежать оператора if и изменчивого аргумента аргумента по умолчанию

+5
30 окт. '17 в 3:26
источник

Gotchas

Да, есть несколько gotchas.

fn() == fn(3) == fn(4, 4)

Во-первых, если fn возвращает 0, вы не можете знать, был ли он вызван без какого-либо параметра, с одним параметром или с несколькими равными параметрами:

>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0

Что означает fn?

Затем Python является динамическим языком. Он не указывает нигде, что делает fn, каков должен быть его вход и как его выход должен выглядеть. Поэтому очень важно правильно назвать функцию. Точно так же аргументы не должны называться args. delta(*numbers) или calculate_range(*numbers) может лучше описать, что должна делать функция.

Ошибки аргумента

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

>>> fn('1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>> fn(1, '2')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: '>' not supported between instances of 'str' and 'int'
>>> fn('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'

Возможная альтернатива

Здесь можно написать функцию в соответствии с "Легче просить прощения, чем разрешение" . Принцип:

def delta(*numbers):
    try:
        return max(numbers) - min(numbers)
    except TypeError:
        raise ValueError("delta should only be called with numerical arguments") from None
    except ValueError:
        raise ValueError("delta should be called with at least one numerical argument") from None

В качестве примера:

>>> delta()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in delta
ValueError: delta should be called with at least one numerical argument
>>> delta(3)
0
>>> delta('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta(3, 4.5)
1.5
>>> delta(3, 5, 7, 2)
5

Если вы действительно не хотите поднимать исключение, когда delta вызывается без какого-либо аргумента, вы можете вернуть некоторое значение, которое невозможно в противном случае (например, -1 или None):

>>> def delta(*numbers):
...     try:
...         return max(numbers) - min(numbers)
...     except TypeError:
...         raise ValueError("delta should only be called with numerical arguments") from None
...     except ValueError:
...         return -1 # or None
... 
>>> 
>>> delta()
-1
+3
30 окт. '17 в 14:58
источник

Да. Это правильное поведение и сравнение.

По крайней мере, в Python A and B возвращает B, если A по существу True, включая if A NOT Null, NOT None NOT пустой контейнер (например, пустой list dict и т.д.). Возвращается A IFF A по существу False или None или Empty или Null.

С другой стороны, A or B возвращает A, если A по существу True, включая if A NOT Null, NOT None NOT пустой контейнер (например, пустой list dict и т.д.), в противном случае он возвращает B.

Легко не заметить (или игнорировать) это поведение, потому что в Python любой non-null непустой объект оценивается как True, рассматривается как логическое.

Например, все следующее будет печатать "True"

if [102]: 
    print "True"
else: 
    print "False"

if "anything that is not empty or None": 
    print "True"
else: 
    print "False"

if {1, 2, 3}: 
    print "True"
else: 
    print "False"

С другой стороны, все следующие будут печатать "False"

if []: 
    print "True"
else: 
    print "False"

if "": 
    print "True"
else: 
    print "False"

if set ([]): 
    print "True"
else: 
    print "False"
0
30 окт. '17 в 3:27
источник

Является ли это законным/надежным стилем или есть какие-то ошибки на этом?

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

>>>example_list = []
>>>print example_list or 'empty list'
empty list

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

Or оператор

Оператор Python Or возвращает первое значение Truth-y или последнее значение и останавливает

And оператор

Оператор Python And возвращает первое значение False-y или последнее значение и останавливает

За кулисами

В python все числа интерпретируются как True за исключением 0. Поэтому, говоря:

0 and 10 

совпадает с:

False and True

Это явно False. Поэтому логично, что он возвращает 0

0
30 окт. '17 в 3:33
источник

понять по-простому,

И: if first_val is False return first_val else second_value

например:

1 and 2 # here it will return 2 because 1 is not False

но,

0 and 2 # will return 0 because first value is 0 i.e False

и => если кто-то ложь, это будет ложью. если оба верны, то только это станет правдой

ИЛИ: if first_val is False return second_val else first_value

причина в том, что если first равен false, проверьте, верно ли 2 или нет.

например:

1 or 2 # here it will return 1 because 1 is not False

но,

0 or 2 # will return 2 because first value is 0 i.e False

или => если кто-то ложь, это будет правдой. поэтому, если первое значение равно false, независимо от того, какое значение 2 предполагается. поэтому он возвращает второе значение, каким оно может быть.

если кто-то правдив, тогда это станет правдой. если оба являются ложными, то это станет ложным.

0
19 июн. '19 в 6:44
источник

Посмотрите другие вопросы по меткам или Задайте вопрос