我想学习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。
这种方式是否合法/可靠,还是存在任何潜在问题?
我想学习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。
这种方式是否合法/可靠,还是存在任何潜在问题?
We will discuss the behaviour of logical operators and
and or
, where they return the first Falsy or Truthy value, respectively. The not
operator always returns a boolean value. See the docs for more information.
The statement
len(args) and max(args) - min(args)
如果args
不为空,则返回max(args) - min(args)
的结果,否则返回0
。这是一种非常"pythonic"(即Python语言风格)且简洁的表达方式,虽然可能更难读懂。通常,它是一个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
在Python中,exp1
和exp2
是任意的Python对象或返回某些对象的表达式。理解逻辑运算符and
和or
的使用的关键在于理解它们不仅仅局限于操作或返回布尔值。任何具有真值的对象都可以在这里进行测试。这包括int
、str
、list
、dict
、tuple
、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
,即假值,将返回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
的数字。如果存在这样的数字,则列表推导式的结果为非空列表,其为真值,因此返回该列表(短路行为在此处生效)。如果不存在这样的数字,则列表推导式的结果为[]
,其为假值。因此,将评估第二个表达式(一个非空字符串),并返回它。使用条件语句,我们可以将这个函数重写为:
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 ...
,此时真的很难为任何一种情况争辩可读性。 - cs95引用自 Python Docs
请注意,
and
和or
都不会将返回的值和类型限制为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
,即只要找到一个 Truthy 表达式,它就会返回该表达式,因为评估其余的表达式是多余的。True
或 False
,而是 Truthy 或 Falsey,无论如何都将评估为 True
或 False
。您可以直接使用该表达式,它仍然可以正常工作。
要了解什么是真值和假值,请查看Patrick Haugh的答案
and 和 or 是布尔逻辑运算符,但它们在比较时返回实际的值之一。使用 and 时,从左到右按布尔上下文评估值。0、''、[]、()、{} 和 None 在布尔上下文中均为假;其他所有值均为真。
如果在布尔上下文中所有值都为真,则 and 返回最后一个值。
>>> 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)
当存在args时,返回max(args)-min(args)
的值,否则返回0,即len(args)
。
是的,有一些需要注意的地方。
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
以简单的方式理解:
并且:如果 first_val 为 False,则返回 first_val,否则返回 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 => 如果任意一个为假,结果就为假。只有两个都为真时,结果才为真。
OR : 如果 first_val 为 False,则返回 second_val,否则返回 first_value
原因是,如果第一个条件为假,则检查第二个条件是否为真。
例如:
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
或者 => 如果任何一个值为假,结果将为真。因此,如果第一个值为假,无论第二个值是什么,它都会返回第二个值。
如果任何一个值为真,则结果将为真。如果两个值都为假,则结果将为假。
是的。这是“and”比较的正确行为。
至少在Python中,如果A本质上是True(包括A不是Null、不是None、不是空容器(如空list、dict等)),则“A and B”返回B。只有当A本质上是False或None或Empty或Null时才返回A。
另一方面,“A or B”返回A,如果A本质上是True(包括A不是Null、不是None、不是空容器(如空list、dict等)),否则返回B。
很容易忽略或忽视这种行为,因为在Python中,任何非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
。已更正。 - emmanuelsa>>>example_list = []
>>>print example_list or 'empty list'
empty list
因此,您可以真正利用它的优势。为了简洁明了,这是我对它的看法:
或
运算符
Python的or
运算符返回第一个真值或最后一个值,并停止。
与
运算符
Python的and
运算符返回第一个假值或最后一个值,并停止。
幕后花絮
在Python中,除了0之外,所有数字都被解释为True
。因此,说:
0 and 10
等同于:
False and True
这显然是False
。因此,它返回0是合乎逻辑的。
and
(以及or
)不仅限于处理或返回布尔值。 - cs95None
或异常) - ilkkachu0
,则无法判断args
是否为空或非空但所有元素相等。 - Especially Lime