Я пытаюсь изучить 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.
Это законный / надежный стиль, или в этом есть какие-то подводные камни?
python
operators
logical-operators
Марчин
источник
источник
and
(а такжеor
) не ограничивается работой или возвратом логических значений.None
или исключения)0
вы не можете определить,args
было ли оно пустым или непустым, но все элементы были равны.Ответы:
TL; DR
Мы начнем с резюмирования поведения двух логических операторов
and
иor
. Эти идиомы лягут в основу нашего обсуждения ниже.Поведение также кратко описано в документации , особенно в этой таблице:
Единственный оператор, возвращающий логическое значение независимо от его операндов, - это
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 красиво резюмирует правдивость в этом посте .
Как
and
работаетМы опираемся на вопрос OP как продолжение обсуждения того, как работают эти операторы в этих случаях.
Найти минимум и максимум очень просто (используйте встроенные функции!). Единственная загвоздка здесь - это правильная обработка углового случая, когда список аргументов может быть пустым (например, вызов
foo()
). Мы можем сделать и то, и другое в одной строке благодаряand
оператору:def foo(*args): return len(args) and max(args) - min(args)
foo(1, 2, 3, 4, 5) # 4 foo() # 0
Поскольку
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
подобным образом на надуманном примере.Здесь мы используем
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
Как и прежде, эта структура более гибкая с точки зрения обработки ошибок.
источник
if ... else (if ... else (if ... else (if ... else ...)))
можно с таким же успехом переписать,... and ... and ... and ... and ...
и в этот момент действительно становится трудно оспорить удобочитаемость в любом случае.Цитата из документов Python
Итак, вот как 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
и выражения, то есть он вернет выражение, которое является истинным, как только найдет его, потому что оценка остальной части выражения является избыточной.Вместо того, чтобы возвращать hardcore
True
orFalse
, Python возвращает Truthy или Falsey , которые в любом случае будут оцениватьTrue
orFalse
. Вы можете использовать выражение как есть, и оно все равно будет работать.Чтобы узнать, что такое Truthy and Falsey , проверьте ответ Патрика Хо
источник
и и / или выполняют логическую логику, но при сравнении они возвращают одно из фактических значений. При использовании и значения оцениваются в логическом контексте слева направо. 0, '', [], (), {} и None являются ложными в логическом контексте; все остальное правда.
Если все значения истинны в логическом контексте, и возвращает последнее значение.
>>> 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)
когда есть аргументы, иначе возвращаетlen(args)
0.источник
Это нормально, это оценка короткого замыкания, при которой возвращается последнее значение.
Вы подаете хороший пример. Функция вернется,
0
если аргументы не переданы, и код не должен проверять особый случай отсутствия переданных аргументов.Другой способ использовать это - установить аргументы None по умолчанию для изменяемого примитива, такого как пустой список:
def fn(alist=None): alist = alist or [] ....
Если ему передается какое-то неверное значение, по
alist
умолчанию используется пустой список, удобный способ избежатьif
оператора и ловушки изменяемого аргумента по умолчаниюисточник
Попался
Да, есть несколько ошибок.
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
операторor
Оператор Python возвращает первое значение Truth-y или последнее значение и останавливаетAnd
операторand
Оператор Python возвращает первое значение 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
if,A
по существу,True
включая ifA
НЕ NULL, НЕ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"
источник
A
по существуTrue
. Исправленный.понять просто,
А ТАКЖЕ :
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
причина в том, что если первое ложно, то проверяется, истинно ли 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
или => если у кого ложь, это будет правда. поэтому, если первое значение ложно, независимо от того, какие 2 значения предполагаются. поэтому он возвращает второе значение, каким бы оно ни было.
если кто-то прав, то это станет правдой. если оба ложны, тогда он станет ложным.
источник