Странное использование оператора 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 ответов:
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
возвращается IFFA
по сути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"