Странное использование оператора python и / или


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

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

7 62

7 ответов:

TL; DR

логическое и (and):

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

логическое или (or):

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


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

это оченьвесть лаконичный способ сказать:

если args не пусто, верните результат max(args) - min(args).

логическое and и or операторы не ограничиваются работой или возвращением логических значений. Здесь можно проверить любой объект со значением истинности. Это включает в себя int,str,list,dict,tuple, set,NoneType, и пользовательские объекты. Правила короткого замыкания по-прежнему применяются.

обратите внимание, что это более краткий способ построения if-else выражение:

return exp1 and exp2

следует (примерно) перевести на:

r1 = exp1
if not r1:
    return r1

return exp2

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

пример and(как показано на OP)

вернуть разницу между min и max группы аргументов.

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).

если foo не получает аргументов, len(args) и 0 что является ложью, и 0 возвращается.

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

def foo(*args):
    if not len(args):
        return 0

    return max(args) - min(args)

пример с or

возвращает все числа 9000.

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 является [], который является Falsey. Таким образом, второе выражение теперь вычисляется (непустая строка) и возвращается.

и альтернативой для этой функции будет:

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

    return r

цитирую Python Docs

обратите внимание, что ни and, ни orограничения the стоимостью и тип они возвращаются к 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 тоже, то есть он вернет выражение, которое истина как только он находит его, причина вычисление остальной части выражения является избыточным.

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


знать истина и Falsey, Регистрация ответ Патрика Хоу

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

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

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

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

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

код

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

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

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

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

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

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

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

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

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

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

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

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

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

Or оператор

в Python or оператор возвращает первую истину-y значение, или последнее значение, и останавливается

And оператор

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

за кулисами

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

0 and 10 

- это то же, что:

False and True

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

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

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

С другой стороны, A or B возвращает A если A по сути True в том числе, если A is Не нуль, не None не пустой контейнер (например, пустой 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"