Странное использование оператора 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в том числе, еслиAis Не нуль, не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"