“and” 和 “or” 如何与非布尔值一起作用?
- 2024-11-22 08:48:00
- admin 原创
- 189
问题描述:
我正在尝试学习 Python,遇到了一些代码,它们很简洁,但不太合理
当时的情况是:
def fn(*args):
return len(args) and max(args)-min(args)
我知道它在做什么,但是为什么 python 会这样做 - 即返回值而不是 True/False?
10 and 7-2
返回 5。类似地,将 and 更改为 or 将导致功能发生变化。因此
10 or 7 - 2
将返回 10。
这是合法/可靠的风格吗,或者是否存在任何陷阱?
解决方案 1:
总结
我们首先总结一下两个逻辑运算符and
和的两种行为or
。这些习语将构成我们下面讨论的基础。
and
如果有的话,返回第一个 Falsy 值,否则返回表达式中的最后一个值。
or
如果有的话,返回第一个真值,否则返回表达式中的最后一个值。
该行为也在文档中进行了总结,特别是在下表中:
手术 | 结果 |
---|---|
x or y | 如果x为假,则y,否则x |
x and y | 如果x为假,则x,否则y |
not x | 如果x为假,则True 否则False |
无论其操作数是什么,唯一返回布尔值的运算符是not
运算符。
“真实性”和“真实性”评估
声明
len(args) and max(args) - min(args)
是一种非常简洁 的 Python 式(可能可读性较差)表示“如果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
和是任意的 Python 对象,或返回某个对象的表达式。理解此处逻辑和运算符用法的exp2
关键在于理解它们不限于操作或返回布尔值。任何具有真值的对象都可以在这里测试。这包括、、、、、、和用户定义的对象。短路规则仍然适用。and
`orint
strlist
dicttuple
set`NoneType
但什么是真实性?
它指的是对象在条件表达式中使用时如何求值。@Patrick Haugh 在这篇文章中很好地总结了真实性。
所有值都被视为“真”,除了以下值,它们是“假”的:
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
。我们使用“真”和“假”来区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
。请注意,如果第一个表达式被求值为真,则返回值始终是第二个表达式的结果。如果第一个表达式被求值为假,则返回的结果是第一个表达式的结果。
在上面的函数中,如果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
我通过一个虚构的例子以类似的方式解释了其工作原理。
给定一个具有定义的函数
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
。如果存在任何这样的数字,列表推导的结果是一个非空列表,即 Truthy,因此返回它(此处短路)。如果不存在这样的数字,则列表推导的结果[]
为 Falsy。因此现在对第二个表达式进行求值(非空字符串)并返回。
使用条件语句,我们可以将此函数重写为:
def foo(*args):
r = [x for x in args if x > 9000]
if not r:
return 'No number over 9000!'
return r
与以前一样,这种结构在错误处理方面更加灵活。
解决方案 2:
引用自Python 文档
请注意, 和 都
and
不会or
限制它们返回的值和类型,而是返回最后评估的参数。这有时很有用,例如,如果是一个字符串,如果它为空,则应将其替换为默认值,表达式将产生所需的值。False
`Trues
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
表达式,它就会返回该表达式,因为评估表达式的其余部分是多余的。
Python不会返回硬核的True
或,而是返回Truthy或Falsey,它们的计算结果无论如何都会是或。您可以按原样使用该表达式,它仍然有效。False
`True`False
要了解什么是Truthy和Falsey,请查看Patrick Haugh 的回答
解决方案 3:
and和or执行布尔逻辑,但它们在比较时会返回实际值之一。使用and时,值在布尔上下文中从左到右进行求值。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。
解决方案 4:
这是合法/可靠的风格吗,或者是否存在任何陷阱?
这是合法的,它是一个短路评估,其中返回最后一个值。
您提供了一个很好的例子。如果没有传递参数,函数将返回0
,并且代码不必检查没有传递参数的特殊情况。
另一种使用方法是将 None 参数默认为可变原语,例如空列表:
def fn(alist=None):
alist = alist or []
....
如果传递了一些非真值,alist
则默认为空列表,这是一种避免if
语句和可变默认参数陷阱的简便方法
解决方案 5:
陷阱
是的,有一些陷阱。
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
解决方案 6:
这是合法/可靠的风格吗,或者是否存在任何陷阱?
我想补充一点,它不仅合法可靠,而且非常实用。这是一个简单的例子:
>>>example_list = []
>>>print example_list or 'empty list'
empty list
因此,您可以真正利用它。为了简洁起见,我是这样看待它的:
Or
操作员
Python 的or
运算符返回第一个真值或最后一个值,然后停止
And
操作员
Python 的and
运算符返回第一个 False-y 值或最后一个值,然后停止
幕后
在python中,所有数字都被解释为True
除了0之外。因此,说:
0 and 10
与以下相同:
False and True
这显然是False
。因此,它返回 0 是合乎逻辑的
解决方案 7:
是的。这是和比较的正确行为。
至少在 Python 中,如果本质上包括如果为 NOT Null、NOT NOT 为 Empty 容器(比如空的、等),A and B
则返回。如果本质上为或或 Empty 或 Null,则返回。B
`ATrue
ANone
listdict
AA
False`None
另一方面,如果本质上包括如果NOT 为 Null、NOT NOT 为 Empty 容器(例如空的、等),则A or B
返回,否则返回。A
`ATrue
ANone
listdict
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"
解决方案 8:
以简单的方式理解,
和 : 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
and =>如果任何一个为假,它就会为假。如果两者都为真,那么只有它才会变为真。
或者 : 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 个值是什么。所以它会返回第二个值,无论它是什么。
如果任何一个是真的,那么它就会变成真的。如果两者都是假的,那么它就会变成假的。