“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?1...

问题描述:

我正在尝试学习 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`orintstrlistdicttupleset`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分值TrueFalse


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`Truess 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或,而是返回TruthyFalsey,它们的计算结果无论如何都会是或。您可以按原样使用该表达式,它仍然有效。False`True`False


要了解什么是TruthyFalsey,请查看Patrick Haugh 的回答

解决方案 3:

andor执行布尔逻辑,但它们在比较时会返回实际值之一。使用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没有任何参数的情况下调用时引发异常,则可以返回一些以其他方式不可能实现的值(例如-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

解决方案 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`ATrueANonelistdictAAFalse`None

另一方面,如果本质上包括如果NOT 为 Null、NOT NOT 为 Empty 容器(例如空的、等),则A or B返回,否则返回。A`ATrueANonelistdictB`

很容易忽略(或忽视)这种行为,因为在 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 个值是什么。所以它会返回第二个值,无论它是什么。

如果任何一个是真的,那么它就会变成真的。如果两者都是假的,那么它就会变成假的。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用