“ and”和“ or”如何处理非布尔值?

我正在尝试学习 python,偶然发现了一些漂亮而简短的代码,但是完全没有意义

背景是:

def fn(*args):
return len(args) and max(args)-min(args)

我知道它在做什么,但是为什么 python 要这样做——即返回值而不是 True/False?

10 and 7-2

类似地,更改和 to 或将导致功能的更改

10 or 7 - 2

将返回10。

这种风格是合法可靠的,还是有什么陷阱?

20351 次浏览

引自 巨蟒文档

请注意,andor都不返回 价值类型 返回到 FalseTrue,而是返回 最后求值的参数 有时是有用的,例如,如果 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也是一样,也就是说,一旦找到它,它将返回表达式 真心话,因为计算表达式的其余部分是多余的。

Python 不返回核心 TrueFalse,而是返回 真心话费尔西,它们的计算结果总是 TrueFalse。你可以按原样使用这个表达式,它仍然可以工作。


要知道什么是 真心话费尔西,请检查 Patrick Haugh 的回答

这种风格是合法可靠的,还是有什么陷阱?

这是合法的,它是返回最后一个值的 短路评估短路评估短路评估

你提供了一个很好的例子。如果没有传递参数,函数将返回 0,代码不必检查没有传递参数的特殊情况。

另一种使用它的方法是将 Nothing 参数默认为一个可变的原语,比如一个空列表:

def fn(alist=None):
alist = alist or []
....

如果一些非真值被传递给 alist,它默认为一个空列表,这是避免 if语句和 可变缺省参数陷阱的简便方法

是的。这是正确的行为和比较。

至少在 Python 中,如果 A本质上是 True,那么 A and B返回 B,包括如果 A不是 Null,则 None不是空容器(例如空 listdict等)。返回的 A基本上是 B0或 None或空或空。

另一方面,如果 A本质上是 True,包括如果 A不是空,则 A or B返回 A,如果 None不是空容器(例如一个空的 listdict等) ,否则返回 B

很容易不注意(或忽略)这种行为,因为在 Python 中,任何计算为 True 的 non-null非空对象都被视为布尔值。

例如,以下所有内容都将打印“ 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"

DR

我们首先总结两个逻辑运算符 andor的两种行为。这些习语将构成我们下面讨论的基础。

and

如果有,返回第一个 Falsy 值,否则返回最后一个 表达式中的。

or

如果有,返回第一个 Truthy 值,否则返回最后一个 表达式中的。

那些文件中也总结了这种行为,特别是在下表中:

行动 结果
x or y 如果 X为假,那么 ,else X
x and y 如果 X为假,那么 X,else
not x 如果 X是假的,那么 True,else False

返回布尔值而不管其操作数是什么的唯一操作符是 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

其中 exp1exp2是任意的 python 对象,或者返回某个对象的表达式。理解这里逻辑 andor操作符的用法的关键是理解它们不仅限于操作或返回布尔值。任何具有真实性价值的对象都可以在这里进行测试。这包括 intstrlistdicttuplesetexp20和用户定义的对象。短路规则仍然适用。

但什么是真实?
它指的是在条件表达式中使用对象时如何求值。@Patrick Haugh 在 这篇文章中很好地总结了真实性。

除了以下值外,所有值都被认为是“真值” “ false”:

  • None
  • False
  • 0
  • 0.0
  • 0j
  • Decimal(0)
  • Fraction(0, 1)
  • 一个空的 list
  • 一个空的 dict
  • 一个空的 tuple
  • 一个空的 str
  • 一个空的 bytes
  • 一个空的 set
  • 一个空的 range,就像 range(0)
  • 对象
    • 返回 False
    • 返回 0

“ true”值将满足 ifwhile执行的检查 我们用“真实”和“虚假”来区分 boolTrueFalse


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,那么第二个表达式也必须求值。注意,如果第一个表达式被求值为 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上的所有数字。如果存在任何这样的数字,列表内涵的结果是一个非空列表,它是真实的,因此它被返回(这里是短路)。如果不存在这样的数字,那么列表比较的结果是 [],它是 Falsy。因此,现在计算第二个表达式(非空字符串)并返回。

使用条件语句,我们可以将这个函数重写为,

def foo(*args):
r = [x for x in args if x > 9000]
if not r:
return 'No number over 9000!'
    

return r

与前面一样,这种结构在错误处理方面更加灵活。

以及 或者执行布尔逻辑,但是它们在比较时返回一个实际值。使用 还有时,从左到右在布尔上下文中计算值。0,”,[] ,() ,{} ,没有在布尔上下文中是假的; 其他的都是真的。

如果在布尔上下文中所有值都为 true,则 还有返回最后一个值。

>>> 2 and 5
5
>>> 2 and 5 and 10
10

如果在布尔上下文中有任何值为 false,则 还有返回第一个 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。

这种风格是合法可靠的,还是有什么陷阱?

我想对这个问题补充一点,它不仅合法可靠,而且非常实用。下面是一个简单的例子:

>>>example_list = []
>>>print example_list or 'empty list'
empty list

为了简洁起见,我是这样看待它的:

Or接线员

Python 的 or运算符返回第一个 Truth-y 值或最后一个值,然后停止

And接线员

Python 的 and运算符返回第一个 False-y 值或最后一个值,然后停止

在幕后

在 python 中,除了0之外,所有数字都被解释为 True。因此,表示:

0 and 10

等同于:

False and True

显然是 False,因此返回0是合乎逻辑的

抓到你了

是的,有一些陷阱。

fn() == fn(3) == fn(4, 4)

首先,如果 fn返回 0,您就不能知道它是在没有任何参数、只有一个参数或者有多个等参数的情况下被调用的:

>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0

fn是什么意思?

那么,Python 是一种动态语言。它没有在任何地方指定 fn做什么、它的输入应该是什么以及它的输出应该是什么样子。因此,正确命名函数非常重要。类似地,参数不一定要被称为 argsdelta(*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时引发异常,你可以返回一些在其他情况下不可能的值(例如 -1None) :

>>> 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

用简单的方式来理解,

AND: 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

如果有人错了,那么他就是错的。如果两者都是真的,那么只有它才是真的

OR: 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是什么值。 所以它会返回第二个值。

如果有人是真的,那么它就会变成真的。如果两者都是假的,那么它就会变成假的。