奇怪地使用"和"/"或"运算符

Mar*_*cin 84 python operators logical-operators

我正在尝试学习python,并且遇到了一些既好又短的代码,但并不完全有意义

背景是:

def fn(*args):
    return len(args) and max(args)-min(args)
Run Code Online (Sandbox Code Playgroud)

我得到它正在做的事情,但为什么python这样做 - 即返回值而不是True/False?

10 and 7-2
Run Code Online (Sandbox Code Playgroud)

返回5.同样,更改和将导致或将导致功能更改.所以

10 or 7 - 2
Run Code Online (Sandbox Code Playgroud)

会回来10.

这是合法/可靠的风格,还是有任何问题?

cs9*_*s95 116

TL; DR

我们通过总结两个逻辑运算符的两个行为开始andor.这些习语将成为我们下面讨论的基础.

and

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

or

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

该行为也在文档中进行总结,尤其是在此表中:

在此输入图像描述

无论操作数如何,唯一返回布尔值的not运算符是运算符.


"真实"和"Truthy"评估

该声明

len(args) and max(args) - min(args)
Run Code Online (Sandbox Code Playgroud)

是一种非常 pythonic简洁(可以说是不太可读)的方式说"如果args不是空的,返回结果max(args) - min(args)",否则返回0.通常,它是if-else表达式的更简洁表示.例如,

exp1 and exp2
Run Code Online (Sandbox Code Playgroud)

应该(粗略地)翻译成:

r1 = exp1
if r1:
    r1 = exp2
Run Code Online (Sandbox Code Playgroud)

或者,等效地,

r1 = exp1 if exp1 else exp2
Run Code Online (Sandbox Code Playgroud)

where exp1exp2是任意python对象,或返回某些对象的表达式.理解逻辑andor运算符的使用的关键是理解它们不限于操作或返回布尔值.任何具有真值的对象都可以在这里测试.这包括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

A"truthy"值将符合执行检查ifwhile 语句.我们使用"truthy"和"falsy"来区 bool分值TrueFalse.


如何and工作

我们以OP的问题为基础,讨论这些运算符在这些情况下的运作方式.

给定定义的函数

exp1 or exp2
Run Code Online (Sandbox Code Playgroud)

如何在零个或多个参数列表中返回最小值和最大值之间的差异?

找到最小值和最大值很容易(使用内置函数!).这里唯一的障碍是适当地处理参数列表可能为空(例如,调用foo())的极端情况.由于and运营商,我们可以在一条线上做到这两点:

r1 = exp1
if not r1:
    r1 = exp2
Run Code Online (Sandbox Code Playgroud)

def foo(*args):
    ...
Run Code Online (Sandbox Code Playgroud)

and使用以来,如果第一个表达式,则还必须评估第二个表达式True.请注意,如果第一个表达式被评估为真实,则返回值始终第二个表达式的结果.如果第一个表达式被评估为Falsey,则返回的结果是第一个表达式的结果.

在上面的函数中,如果foo接收到一个或多个参数,len(args)则大于0(正数),因此返回的结果是max(args) - min(args).OTOH,如果没有参数传递,len(args)0这是Falsey,并0返回.

请注意,编写此函数的另一种方法是:

def foo(*args):
     return len(args) and max(args) - min(args)
Run Code Online (Sandbox Code Playgroud)

或者,更简洁,

foo(1, 2, 3, 4, 5)
# 4

foo()
# 0
Run Code Online (Sandbox Code Playgroud)

如果当然,这些函数都不执行任何类型检查,因此除非您完全信任所提供的输入,否则不要依赖这些结构的简单性.


如何or工作

or用一个人为的例子以类似的方式解释了它的工作原理.

给定定义的函数

def foo(*args):
    if not len(args):
        return 0

    return max(args) - min(args)
Run Code Online (Sandbox Code Playgroud)

你将如何完成foo返回所有数字9000

我们or在这里用来处理角落的情况.我们定义foo为:

def foo(*args):
    return 0 if not args else max(args) - min(args)
Run Code Online (Sandbox Code Playgroud)

foo在列表上执行过滤以保留所有数字9000.如果存在任何这样的数字,列表推导的结果是一个非空的列表,它是Truthy,因此返回(这里的短路动作).如果不存在这样的数字,则列表comp的结果[]是Falsey.因此,现在评估第二个表达式(非空字符串)并返回.

使用条件,我们可以重写这个函数,

def foo(*args):
    ...
Run Code Online (Sandbox Code Playgroud)

和以前一样,这种结构在错误处理方面更加灵活.

  • 为简洁而牺牲所有清晰度并不是"pythonic",我认为这就是这种情况.这不是一个简单的结构. (32认同)
  • @Baldrickk在人们开始抨击这种语法以支持三元运算符之前,请记住,当涉及到n-ary条件表达式时,三元运算符可能会很快失控.例如,`if ... else(if ... else(if ... else(if ... else ...)))`也可以重写为`...和... ......和......以及......在这一点上,任何一种情况都难以为可读性辩护. (10认同)
  • 我认为应该注意到Python条件表达式使这种语法不太常见.我当然更喜欢max(args) - min(args)如果len(args)其他0到原始. (9认同)
  • 为简洁而牺牲清晰度并不是pythonic,但这不是这样做的.这是一个众所周知的习语.这是一个你必须学习的习语,就像任何其他成语一样,但它很难"牺牲清晰度". (4认同)
  • 另一个常见的混淆是,如果不存在则分配一个值:"some_var = arg或3" (3认同)

Ami*_*oki 15

引用Python文档

需要注意的是既不and也没有or 限制价值类型,他们返回FalseTrue,而是返回最后一个变量.这有时是有用的,例如,如果s是一个字符串,如果它是空的,应该用默认值替换,表达式s or 'foo'产生所需的值.

所以,这就是Python设计用于评估布尔表达式的方法,上面的文档让我们深入了解了为什么这样做.

要获得布尔值,只需对其进行类型转换即可.

return bool(len(args) and max(args)-min(args))
Run Code Online (Sandbox Code Playgroud)

为什么?

短路.

例如:

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
Run Code Online (Sandbox Code Playgroud)

同样or也是如此,也就是说,一旦找到它就会返回Truthy的表达式,因为评估表达式的其余部分是多余的.

而不是返回铁杆True或者False,巨蟒返回TruthyFalsey,这是无论如何要评估为TrueFalse.您可以按原样使用表达式,它仍然可以使用.


要知道什么是TruthyFalsey,检查帕特里克·哈夫的答案


Nit*_*ese 7

/执行布尔逻辑,但它们在比较时返回一个实际值.使用和时,从左到右在布尔上下文中计算值.布尔上下文中0,'',[],(),{}None为false; 其他一切都是真的.

如果所有的值在布尔上下文是真实的,并且返回最后一个值.

>>> 2 and 5
5
>>> 2 and 5 and 10
10
Run Code Online (Sandbox Code Playgroud)

如果布尔上下文中的任何值为false ,则返回第一个false值.

>>> '' and 5
''
>>> 2 and 0 and 5
0
Run Code Online (Sandbox Code Playgroud)

所以代码

return len(args) and max(args)-min(args)
Run Code Online (Sandbox Code Playgroud)

返回max(args)-min(args)args的值返回时 返回len(args)0.


sal*_*ise 5

这是合法/可靠的风格,还是有任何问题?

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

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

使用它的另一种方法是默认无参数可变基元,如空列表:

def fn(alist=None):
    alist = alist or []
    ....
Run Code Online (Sandbox Code Playgroud)

如果传递一些非真值,alist则默认为空列表,避免if语句和可变默认参数陷阱的方便方法