TL;DR
我们首先总结两个逻辑运算符and 和or 的两种行为。这些习语将构成我们下面讨论的基础。
and
如果有则返回第一个 Falsy 值,否则返回最后一个
表达式中的值。
or
如果有则返回第一个 Truthy 值,否则返回最后一个
表达式中的值。
the docs 中也总结了该行为,特别是在此表中:
无论其操作数如何,唯一返回布尔值的运算符是not 运算符。
“真实性”和“真实性”评价
声明
len(args) and max(args) - min(args)
是一种非常 pythonic 简洁(并且可以说可读性较差)的方式来表示“如果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 在this post 中很好地总结了真实性。
所有值都被认为是“真实的”,但以下除外,它们是
“假”:
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 执行的检查
陈述。我们使用“truthy”和“falsy”来区分
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,则还必须计算第二个表达式。请注意,如果第一个表达式被评估为真,则返回值总是第二个表达式的结果。如果第一个表达式被评估为 Falsy,那么返回的结果就是第一个表达式的结果。
在上面的函数中,如果foo接收到一个或多个参数,len(args)大于0(一个正数),所以返回的结果是max(args) - min(args)。 OTOH,如果没有传递任何参数,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的工作原理。
给定一个具有定义的函数
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
和以前一样,这种结构在错误处理方面更加灵活。